comparison nnchat.c @ 168:2e4850ece456

Partially re-factor connection handling.
author Matti Hamalainen <ccr@tnsp.org>
date Sun, 14 Nov 2010 22:08:08 +0200
parents f2f0b6f9281b
children 8d4cdbeae606
comparison
equal deleted inserted replaced
167:b0b63d164702 168:2e4850ece456
27 #endif 27 #endif
28 28
29 #define SET_MIN_BACKBUF (1024) /* Backbuffer size (in lines) */ 29 #define SET_MIN_BACKBUF (1024) /* Backbuffer size (in lines) */
30 #define SET_MAX_HISTORY (16) /* Command history length */ 30 #define SET_MAX_HISTORY (16) /* Command history length */
31 #define SET_DELAY (15) 31 #define SET_DELAY (15)
32 #define SET_DELAY_USEC (SET_DELAY * 250)
33 #define SET_KEEPALIVE (15*60) /* Ping/keepalive period in seconds */ 32 #define SET_KEEPALIVE (15*60) /* Ping/keepalive period in seconds */
34 33
35 34
36 /* Options 35 /* Options
37 */ 36 */
358 THERR_V(fmt, ap2); 357 THERR_V(fmt, ap2);
359 va_end(ap2); 358 va_end(ap2);
360 } 359 }
361 360
362 361
363 int handleUser(int sock, const char *str) 362 int handleUser(nn_conn_t *conn, const char *str)
364 { 363 {
365 const char *msg = "</USER><MESSAGE>", *p = str; 364 const char *msg = "</USER><MESSAGE>", *p = str;
366 char *q, *s, *t, *h, *p2; 365 char *q, *s, *t, *h, *p2;
367 366
368 (void) sock; 367 (void) conn;
369 368
370 s = strstr(str, msg); 369 s = strstr(str, msg);
371 if (!s) return 1; 370 if (!s) return 1;
372 *s = 0; 371 *s = 0;
373 s += strlen(msg); 372 s += strlen(msg);
426 th_free(p2); 425 th_free(p2);
427 return 0; 426 return 0;
428 } 427 }
429 428
430 429
431 int handleLogin(int sock, const char *str) 430 int handleLogin(nn_conn_t *conn, const char *str)
432 { 431 {
433 char tmpStr[256]; 432 char tmpStr[256];
434 433
435 getTimeStamp(tmpStr, sizeof(tmpStr), "%c"); 434 getTimeStamp(tmpStr, sizeof(tmpStr), "%c");
436 435
437 if (!strncmp(str, "FAILURE", 7)) { 436 if (!strncmp(str, "FAILURE", 7)) {
438 printMsg("½1½Login failure½0½ - ½3½%s½0½\n", tmpStr); 437 printMsg("½1½Login failure½0½ - ½3½%s½0½\n", tmpStr);
439 return -2; 438 return -2;
440 } else if (!strncmp(str, "SUCCESS", 7)) { 439 } else if (!strncmp(str, "SUCCESS", 7)) {
441 printMsg("½2½Login success½0½ - ½3½%s½0½\n", tmpStr); 440 printMsg("½2½Login success½0½ - ½3½%s½0½\n", tmpStr);
442 nn_send_msg(sock, optUserName2, "%%2FRequestUserList"); 441 nn_conn_send_msg(conn, optUserName2, "%%2FRequestUserList");
443 return 0; 442 return 0;
444 } else 443 } else
445 return 1; 444 return 1;
446 } 445 }
447 446
448 447
449 int handleAddUser(int sock, const char *str) 448 int handleAddUser(nn_conn_t *conn, const char *str)
450 { 449 {
451 char *p, *s = strstr(str, "</ADD_USER>"); 450 char *p, *s = strstr(str, "</ADD_USER>");
452 451
453 (void) sock; 452 (void) conn;
454 453
455 if (!s) return 1; 454 if (!s) return 1;
456 *s = 0; 455 *s = 0;
457 456
458 p = nn_dbldecode_str(str); 457 p = nn_dbldecode_str(str);
464 th_free(p); 463 th_free(p);
465 return 0; 464 return 0;
466 } 465 }
467 466
468 467
469 int handleDeleteUser(int sock, const char *str) 468 int handleDeleteUser(nn_conn_t *conn, const char *str)
470 { 469 {
471 char *p, *s = strstr(str, "</DELETE_USER>"); 470 char *p, *s = strstr(str, "</DELETE_USER>");
472 471
473 (void) sock; 472 (void) conn;
474 473
475 if (!s) return 1; 474 if (!s) return 1;
476 *s = 0; 475 *s = 0;
477 476
478 p = nn_dbldecode_str(str); 477 p = nn_dbldecode_str(str);
484 th_free(p); 483 th_free(p);
485 return 0; 484 return 0;
486 } 485 }
487 486
488 487
489 int handleFoo(int sock, const char *str) 488 int handleFoo(nn_conn_t *conn, const char *str)
490 { 489 {
491 (void) sock; (void) str; 490 (void) conn; (void) str;
492 491
493 return 0; 492 return 0;
494 } 493 }
495 494
496 495
497 int handleBoot(int sock, const char *str) 496 int handleBoot(nn_conn_t *conn, const char *str)
498 { 497 {
499 (void) sock; (void) str; 498 (void) conn; (void) str;
500 errorMsg("Booted by server.\n"); 499 errorMsg("Booted by server.\n");
501 return -1; 500 return -1;
502 } 501 }
503 502
504 503
505 typedef struct { 504 typedef struct {
506 char *cmd; 505 char *cmd;
507 ssize_t len; 506 ssize_t len;
508 int (*handler)(int, const char *); 507 int (*handler)(nn_conn_t *, const char *);
509 } protocmd_t; 508 } protocmd_t;
510 509
511 510
512 static protocmd_t protoCmds[] = { 511 static protocmd_t protoCmds[] = {
513 { "<USER>", -1, handleUser }, 512 { "<USER>", -1, handleUser },
519 }; 518 };
520 519
521 static const int nprotoCmds = sizeof(protoCmds) / sizeof(protoCmds[0]); 520 static const int nprotoCmds = sizeof(protoCmds) / sizeof(protoCmds[0]);
522 521
523 522
524 int handleProtocol(const int sock, const char *buf, const ssize_t bufLen) 523 int handleProtocol(nn_conn_t *conn, const char *buf, const ssize_t bufLen)
525 { 524 {
526 static BOOL protoCmdsInit = FALSE; 525 static BOOL protoCmdsInit = FALSE;
527 int i; 526 int i;
528 527
529 if (!protoCmdsInit) { 528 if (!protoCmdsInit) {
533 } 532 }
534 533
535 for (i = 0; i < nprotoCmds; i++) { 534 for (i = 0; i < nprotoCmds; i++) {
536 ssize_t cmdLen = protoCmds[i].len; 535 ssize_t cmdLen = protoCmds[i].len;
537 if (cmdLen < bufLen && !strncmp(buf, protoCmds[i].cmd, cmdLen)) 536 if (cmdLen < bufLen && !strncmp(buf, protoCmds[i].cmd, cmdLen))
538 return protoCmds[i].handler(sock, buf + cmdLen); 537 return protoCmds[i].handler(conn, buf + cmdLen);
539 } 538 }
540 539
541 if (optDebug) { 540 if (optDebug) {
542 printMsg("Unknown protocmd: \"%s\"\n", buf); 541 printMsg("Unknown protocmd: \"%s\"\n", buf);
543 return 0; 542 return 0;
554 int compareUsername(const void *s1, const void *s2) 553 int compareUsername(const void *s1, const void *s2)
555 { 554 {
556 return strcasecmp((char *) s1, (char *) s2); 555 return strcasecmp((char *) s1, (char *) s2);
557 } 556 }
558 557
559 int handleUserInput(const int sock, char *buf, size_t bufLen) 558 int handleUserInput(nn_conn_t *conn, char *buf, size_t bufLen)
560 { 559 {
561 char *tmpStr, tmpBuf[4096]; 560 char *tmpStr, tmpBuf[4096];
562 BOOL result; 561 BOOL result;
563 562
564 /* Trim right */ 563 /* Trim right */
577 printMsg("Invalid color value '%s'\n", buf+7); 576 printMsg("Invalid color value '%s'\n", buf+7);
578 return 1; 577 return 1;
579 } 578 }
580 optUserColor = tmpInt; 579 optUserColor = tmpInt;
581 printMsg("Setting color to #%06x\n", optUserColor); 580 printMsg("Setting color to #%06x\n", optUserColor);
582 nn_send_msg(sock, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); 581 nn_conn_send_msg(conn, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor);
583 return 0; 582 return 0;
584 } else if (!strncasecmp(buf, "/ignore", 7)) { 583 } else if (!strncasecmp(buf, "/ignore", 7)) {
585 char *name = trimLeft(buf + 7); 584 char *name = trimLeft(buf + 7);
586 if (strlen(name) > 0) { 585 if (strlen(name) > 0) {
587 /* Add or remove someone to/from ignore */ 586 /* Add or remove someone to/from ignore */
673 } 672 }
674 673
675 /* Send double-encoded */ 674 /* Send double-encoded */
676 tmpStr = nn_dblencode_str(buf); 675 tmpStr = nn_dblencode_str(buf);
677 if (tmpStr == 0) return -2; 676 if (tmpStr == 0) return -2;
678 result = nn_send_msg(sock, optUserName2, "%s", tmpStr); 677 result = nn_conn_send_msg(conn, optUserName2, "%s", tmpStr);
679 th_free(tmpStr); 678 th_free(tmpStr);
680 679
681 return result ? 0 : -1; 680 return result ? 0 : -1;
682 } 681 }
683 682
802 } 801 }
803 802
804 return FALSE; 803 return FALSE;
805 } 804 }
806 805
806
807 int main(int argc, char *argv[]) 807 int main(int argc, char *argv[])
808 { 808 {
809 int tmpSocket = -1, curVis = ERR, updateCount = 0; 809 nn_conn_t *conn = NULL;
810 int curVis = ERR, updateCount = 0;
810 struct hostent *tmpHost; 811 struct hostent *tmpHost;
811 BOOL argsOK, isError = FALSE, 812 BOOL argsOK, isError = FALSE,
812 exitProg = FALSE, 813 exitProg = FALSE,
813 colorSet = FALSE, 814 colorSet = FALSE,
814 cursesInit = FALSE, 815 cursesInit = FALSE,
815 networkInit = FALSE, 816 networkInit = FALSE,
816 insertMode = TRUE; 817 insertMode = TRUE;
817 time_t prevTime; 818 time_t prevTime;
818 struct timeval socktv;
819 fd_set sockfds;
820 char *tmpStr; 819 char *tmpStr;
821 nn_editbuf_t *editBuf = nn_editbuf_new(SET_BUFSIZE); 820 nn_editbuf_t *editBuf = nn_editbuf_new(NN_TMPBUF_SIZE);
822 nn_editbuf_t *histBuf[SET_MAX_HISTORY+2]; 821 nn_editbuf_t *histBuf[SET_MAX_HISTORY+2];
823 int histPos = 0, histMax = 0; 822 int histPos = 0, histMax = 0;
824 823
825 cfgitem_t *tmpcfg; 824 cfgitem_t *tmpcfg;
826 char *homeDir = NULL; 825 char *homeDir = NULL;
924 THMSG(2, "True hostname: %s\n", tmpHost->h_name); 923 THMSG(2, "True hostname: %s\n", tmpHost->h_name);
925 924
926 /* To emulate the official client, we first make a request for 925 /* To emulate the official client, we first make a request for
927 * policy file, even though we don't use it for anything... 926 * policy file, even though we don't use it for anything...
928 */ 927 */
929 if ((tmpSocket = nn_open_connection((struct in_addr *) tmpHost->h_addr, 843)) < 0) { 928 if ((conn = nn_conn_open((struct in_addr *) tmpHost->h_addr, 843)) == NULL) {
930 THERR("Policy file request connection setup failed!\n"); 929 THERR("Policy file request connection setup failed!\n");
931 goto err_exit; 930 goto err_exit;
932 } 931 }
933 932 if (!nn_conn_check(conn))
933 goto err_exit;
934
934 tmpStr = "<policy-file-request/>"; 935 tmpStr = "<policy-file-request/>";
935 if (nn_send_to_socket(tmpSocket, tmpStr, strlen(tmpStr) + 1) == FALSE) { 936 if (nn_conn_send_buf(conn, tmpStr, strlen(tmpStr) + 1) == FALSE) {
936 THERR("Failed to send policy file request.\n"); 937 THERR("Failed to send policy file request.\n");
937 goto err_exit; 938 goto err_exit;
938 } else { 939 } else {
939 ssize_t gotBuf; 940 int cres = nn_conn_pull(conn);
940 char tmpBuf[SET_BUFSIZE]; 941 if (cres == 0) {
941 gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0); 942 THMSG(2, "Probe got: %s\n", conn->buf);
942 tmpBuf[gotBuf-1] = 0; 943 } else {
943 THMSG(2, "Probe got: %s\n", tmpBuf); 944 THMSG(2, "Could not get policy probe.\n");
944 nn_close_connection(tmpSocket); 945 }
945 } 946 }
947 nn_conn_close(conn);
946 948
947 /* Okay, now do the proper connection ... */ 949 /* Okay, now do the proper connection ... */
948 if ((tmpSocket = nn_open_connection((struct in_addr *) tmpHost->h_addr, optPort)) < 0) { 950 if ((conn = nn_conn_open((struct in_addr *) tmpHost->h_addr, optPort)) == NULL) {
949 THERR("Main connection setup failed!\n"); 951 THERR("Main connection setup failed!\n");
950 goto err_exit; 952 goto err_exit;
951 } 953 }
954 if (!nn_conn_check(conn))
955 goto err_exit;
952 956
953 THMSG(1, "Connected, logging in as '%s', site '%s'.\n", optUserName, optSite); 957 THMSG(1, "Connected, logging in as '%s', site '%s'.\n", optUserName, optSite);
954 optUserName2 = nn_dblencode_str(optUserName); 958 optUserName2 = nn_dblencode_str(optUserName);
955 tmpStr = nn_dblencode_str(optSite); 959 tmpStr = nn_dblencode_str(optSite);
956 nn_send_msg(tmpSocket, optUserName2, "%%2Flogin%%20%%2Dsite%%20%s%%20%%2Dpassword%%20%s", tmpStr, optPassword); 960 nn_conn_send_msg(conn, optUserName2, "%%2Flogin%%20%%2Dsite%%20%s%%20%%2Dpassword%%20%s", tmpStr, optPassword);
957 th_free(tmpStr); 961 th_free(tmpStr);
958 962
959 /* Initialize NCurses */ 963 /* Initialize NCurses */
960 if (!optDaemon) { 964 if (!optDaemon) {
961 if (LINES < 0 || LINES > 1000) LINES = 24; 965 if (LINES < 0 || LINES > 1000) LINES = 24;
999 updateStatus(insertMode); 1003 updateStatus(insertMode);
1000 } 1004 }
1001 1005
1002 /* Enter mainloop */ 1006 /* Enter mainloop */
1003 prevTime = time(NULL); 1007 prevTime = time(NULL);
1004 FD_ZERO(&sockfds);
1005 FD_SET(tmpSocket, &sockfds);
1006 1008
1007 while (!isError && !exitProg) { 1009 while (!isError && !exitProg) {
1008 int result; 1010 int cres = nn_conn_pull(conn);
1009 fd_set tmpfds; 1011 if (cres == 0) {
1010 1012 do {
1011 /* Check for incoming data from the server */ 1013 size_t bufLen = strlen(conn->ptr) + 1;
1012 socktv.tv_sec = 0; 1014 int result = handleProtocol(conn, conn->ptr, bufLen);
1013 socktv.tv_usec = SET_DELAY_USEC; 1015
1014 tmpfds = sockfds; 1016 if (result > 0) {
1015 if ((result = select(tmpSocket+1, &tmpfds, NULL, NULL, &socktv)) == -1) { 1017 /* Couldn't handle the message for some reason */
1016 int res = nn_get_socket_errno(); 1018 printMsg("Could not handle: %s\n", conn->ptr);
1017 if (res != EINTR) { 1019 } else if (result < 0) {
1018 errorMsg("Error occured in select(sockfds): %d, %s\n", 1020 /* Fatal error, quit */
1019 res, nn_get_socket_errstr(res)); 1021 errorMsg("Fatal error with message: %s\n", conn->ptr);
1020 isError = TRUE; 1022 isError = TRUE;
1021 } 1023 }
1022 } else if (FD_ISSET(tmpSocket, &tmpfds)) { 1024
1023 ssize_t gotBuf; 1025 conn->got -= bufLen;
1024 char tmpBuf[8192]; 1026 conn->ptr += bufLen;
1025 char *bufPtr = tmpBuf; 1027 } while (conn->got > 0 && !isError);
1026 gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0); 1028 }
1027 1029 if (!nn_conn_check(conn))
1028 if (gotBuf < 0) { 1030 isError = TRUE;
1029 int res = nn_get_socket_errno(); 1031
1030 errorMsg("Error in recv: %d, %s\n", res, nn_get_socket_errstr(res));
1031 isError = TRUE;
1032 } else if (gotBuf == 0) {
1033 errorMsg("Server closed connection.\n");
1034 isError = TRUE;
1035 } else {
1036 /* Handle protocol data */
1037 tmpBuf[gotBuf] = 0;
1038 do {
1039 size_t bufLen = strlen(bufPtr) + 1;
1040 result = handleProtocol(tmpSocket, bufPtr, bufLen);
1041
1042 if (result > 0) {
1043 /* Couldn't handle the message for some reason */
1044 printMsg("Could not handle: %s\n", tmpBuf);
1045 } else if (result < 0) {
1046 /* Fatal error, quit */
1047 errorMsg("Fatal error with message: %s\n", tmpBuf);
1048 isError = TRUE;
1049 }
1050
1051 gotBuf -= bufLen;
1052 bufPtr += bufLen;
1053 } while (gotBuf > 0 && !isError);
1054 updateStatus(insertMode);
1055 }
1056 }
1057
1058 /* Handle user input */ 1032 /* Handle user input */
1059 if (!optDaemon) { 1033 if (!optDaemon) {
1060 int c, cnt = 0; 1034 int c, cnt = 0;
1061 BOOL update = FALSE; 1035 BOOL update = FALSE;
1062 1036
1101 case KEY_ENTER: 1075 case KEY_ENTER:
1102 case '\n': 1076 case '\n':
1103 case '\r': 1077 case '\r':
1104 /* Call the user input handler */ 1078 /* Call the user input handler */
1105 if (editBuf->len > 0) { 1079 if (editBuf->len > 0) {
1080 int result;
1106 1081
1107 if (histMax > 0) { 1082 if (histMax > 0) {
1108 nn_editbuf_free(histBuf[SET_MAX_HISTORY+1]); 1083 nn_editbuf_free(histBuf[SET_MAX_HISTORY+1]);
1109 histBuf[SET_MAX_HISTORY+1] = NULL; 1084 histBuf[SET_MAX_HISTORY+1] = NULL;
1110 memmove(&histBuf[2], &histBuf[1], histMax * sizeof(histBuf[0])); 1085 memmove(&histBuf[2], &histBuf[1], histMax * sizeof(histBuf[0]));
1113 histPos = 0; 1088 histPos = 0;
1114 histBuf[1] = nn_editbuf_copy(editBuf); 1089 histBuf[1] = nn_editbuf_copy(editBuf);
1115 if (histMax < SET_MAX_HISTORY) histMax++; 1090 if (histMax < SET_MAX_HISTORY) histMax++;
1116 1091
1117 nn_editbuf_insert(editBuf, editBuf->len, 0); 1092 nn_editbuf_insert(editBuf, editBuf->len, 0);
1118 result = handleUserInput(tmpSocket, editBuf->data, editBuf->len); 1093 result = handleUserInput(conn, editBuf->data, editBuf->len);
1119 1094
1120 nn_editbuf_clear(editBuf); 1095 nn_editbuf_clear(editBuf);
1121 1096
1122 if (result < 0) { 1097 if (result < 0) {
1123 errorMsg("Fatal error handling user input: %s\n", editBuf->data); 1098 errorMsg("Fatal error handling user input: %s\n", editBuf->data);
1259 } /* !optDaemon */ 1234 } /* !optDaemon */
1260 1235
1261 if (++updateCount > 10) { 1236 if (++updateCount > 10) {
1262 time_t tmpTime = time(NULL); 1237 time_t tmpTime = time(NULL);
1263 if (tmpTime - prevTime > SET_KEEPALIVE) { 1238 if (tmpTime - prevTime > SET_KEEPALIVE) {
1264 nn_send_msg(tmpSocket, optUserName2, "/listallusers"); 1239 nn_conn_send_msg(conn, optUserName2, "/listallusers");
1265 prevTime = tmpTime; 1240 prevTime = tmpTime;
1266 } 1241 }
1267 1242
1268 if (!colorSet) { 1243 if (!colorSet) {
1269 colorSet = TRUE; 1244 colorSet = TRUE;
1270 printMsg("%s v%s - %s\n", th_prog_name, th_prog_version, th_prog_fullname); 1245 printMsg("%s v%s - %s\n", th_prog_name, th_prog_version, th_prog_fullname);
1271 printMsg("%s\n", th_prog_author); 1246 printMsg("%s\n", th_prog_author);
1272 printMsg("%s\n", th_prog_license); 1247 printMsg("%s\n", th_prog_license);
1273 nn_send_msg(tmpSocket, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); 1248 nn_conn_send_msg(conn, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor);
1274 } 1249 }
1275 1250
1276 updateStatus(insertMode); 1251 updateStatus(insertMode);
1277 updateCount = 0; 1252 updateCount = 0;
1278 } 1253 }
1299 THMSG(1, "Error exit.\n"); 1274 THMSG(1, "Error exit.\n");
1300 } 1275 }
1301 1276
1302 th_free(optUserName2); 1277 th_free(optUserName2);
1303 1278
1304 nn_close_connection(tmpSocket); 1279 nn_conn_close(conn);
1305 1280
1306 if (networkInit) 1281 if (networkInit)
1307 nn_network_close(); 1282 nn_network_close();
1308 1283
1309 THMSG(1, "Connection terminated.\n"); 1284 THMSG(1, "Connection terminated.\n");