Mercurial > hg > nnchat
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"); |