Mercurial > hg > nnchat
view nnchat.c @ 45:ceec75116aa7
Improved editing commands ctrl+<left/right>
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 28 Oct 2008 00:33:10 +0200 |
parents | 40ba8cdcf03a |
children | 65b1ac6a1e2e |
line wrap: on
line source
/* * NNChat - Custom chat client for NewbieNudes.com chatrooms * Written by Matti 'ccr' Hämäläinen * (C) Copyright 2008 Tecnic Software productions (TNSP) */ #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <sys/time.h> #include <netdb.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include "th_args.h" #include "th_string.h" #include <string.h> #include <errno.h> #include <time.h> #include <ncurses.h> #define SET_MAX_HISTORY (16) #define SET_BUFSIZE (4096) #define SET_ALLOC_SIZE (128) #define SET_DELAY (15) #define SET_DELAY_USEC (SET_DELAY * 1000) /* Options */ int optPort = 8005; int optUserColor = 0x006080; char *optServer = "www11.servemedata.com", *optUserName = NULL, *optUserName2 = NULL, *optPassword = NULL, *optLogFilename = NULL, *setTarget = NULL, *optSite = "NN"; BOOL optDaemon = FALSE; FILE *optLogFile = NULL; WINDOW *mainWin = NULL, *statusWin = NULL, *editWin = NULL; BOOL setPrvMode = FALSE, setMorseMode = FALSE; /* Arguments */ optarg_t optList[] = { { 0, '?', "help", "Show this help", OPT_NONE }, { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, { 2, 'p', "port", "Connect to port", OPT_ARGREQ }, { 3, 's', "server", "Server to connect to", OPT_ARGREQ }, { 4, 'C', "color", "Initial color in RGB hex 000000", OPT_ARGREQ }, { 5, 'l', "logfile", "Log filename", OPT_ARGREQ }, { 6, 'D', "daemon", "A pseudo-daemon mode for logging", OPT_NONE }, { 7, 'S', "site", "Site (default: NN)", OPT_ARGREQ }, }; const int optListN = (sizeof(optList) / sizeof(optList[0])); void argShowHelp() { th_args_help(stdout, optList, optListN, th_prog_name, "[options] <username> <password>"); } int getColor(char *str) { char *p = str; int len, val = 0; for (len = 0; *p && len < 6; p++, len++) { if (*p >= '0' && *p <= '9') { val *= 16; val += (*p - '0'); } else if (*p >= 'A' && *p <= 'F') { val *= 16; val += (*p - 'A') + 10; } else if (*p >= 'a' && *p <= 'f') { val *= 16; val += (*p - 'a') + 10; } else return -1; } return (len == 6) ? val : -1; } BOOL argHandleOpt(const int optN, char *optArg, char *currArg) { switch (optN) { case 0: argShowHelp(); exit(0); break; case 1: th_verbosityLevel++; break; case 2: optPort = atoi(optArg); break; case 3: optServer = optArg; break; case 4: if ((optUserColor = getColor(optArg)) < 0) { THERR("Invalid color argument '%s', should be a RGB hex triplet '000000'.\n", optArg); return FALSE; } THMSG(1, "Using color #%06x\n", optUserColor); break; case 5: optLogFilename = optArg; break; case 7: optSite = optArg; break; case 6: optDaemon = TRUE; THMSG(1, "Running in pseudo-daemon mode.\n"); break; default: THERR("Unknown option '%s'.\n", currArg); return FALSE; } return TRUE; } BOOL argHandleFile(char *currArg) { if (!optUserName) optUserName = currArg; else if (!optPassword) optPassword = currArg; else { THERR("Username '%s' already specified on commandline!\n", optUserName); return FALSE; } return TRUE; } typedef struct { ssize_t pos, len, size; char *data; } editbuf_t; int writeBuf(editbuf_t *buf, ssize_t pos, int ch) { /* Check arguments */ if (buf->len+1 >= buf->size) return -3; if (pos < 0) return -1; else if (pos >= buf->len) { buf->data[buf->len++] = ch; } else { buf->data[pos] = ch; } return 0; } int insertBuf(editbuf_t *buf, ssize_t pos, int ch) { /* Check arguments */ if (buf->len+1 >= buf->size) return -3; if (pos < 0) return -1; else if (pos >= buf->len) { buf->data[buf->len] = ch; } else { memmove(&(buf->data[pos+1]), &(buf->data[pos]), buf->len - pos + 1); buf->data[pos] = ch; } buf->len++; return 0; } int deleteBuf(editbuf_t *buf, ssize_t pos) { /* Check arguments */ if (pos < 0) return -1; else if (pos < buf->len) { memmove(&(buf->data[pos]), &(buf->data[pos+1]), buf->len - pos); buf->len--; return 0; } else return -2; } void clearBuf(editbuf_t *buf) { buf->len = 0; buf->pos = 0; } editbuf_t * newBuf(ssize_t n) { editbuf_t *res = th_calloc(1, sizeof(editbuf_t)); res->data = (char *) th_malloc(n); res->size = n; return res; } void freeBuf(editbuf_t *buf) { if (buf) { th_free(buf->data); th_free(buf); } } editbuf_t * copyBuf(editbuf_t *src) { editbuf_t *res; assert(src != NULL); if (src == NULL) return NULL; if ((res = newBuf(src->size)) == NULL) return NULL; memcpy(res->data, src->data, src->size); res->pos = res->len = src->len; return res; } void setBufPos(editbuf_t *buf, ssize_t pos) { /* Check arguments */ if (pos < 0) buf->pos = 0; else if (pos >= buf->len) buf->pos = buf->len; else buf->pos = pos; } void updateStatus(BOOL insertMode) { char tmpStr[128] = ""; time_t timeStamp; struct tm *tmpTime;; if (statusWin == NULL) return; timeStamp = time(NULL); if ((tmpTime = localtime(&timeStamp)) != NULL) { strftime(tmpStr, sizeof(tmpStr), "%H:%M:%S", tmpTime); } wbkgdset(statusWin, 0x0d00); werase(statusWin); wattrset(statusWin, A_BOLD); mvwaddstr(statusWin, 0, 1, tmpStr); waddstr(statusWin, " | "); wattrset(statusWin, A_BOLD | COLOR_PAIR(16)); waddstr(statusWin, optUserName); wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); waddstr(statusWin, " | "); wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); waddstr(statusWin, insertMode ? "INS" : "DEL"); wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); waddstr(statusWin, " | Prv: "); wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); waddstr(statusWin, setTarget != NULL ? setTarget : "-"); wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); waddstr(statusWin, " | P/C: "); wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); snprintf(tmpStr, sizeof(tmpStr), "%d / #%06x", optPort, optUserColor); waddstr(statusWin, tmpStr); wrefresh(statusWin); } void printEditBuf(char *str, editbuf_t *buf) { if (statusWin == NULL || buf == NULL) return; buf->data[buf->len] = 0; werase(editWin); wattrset(editWin, A_BOLD); mvwaddstr(editWin, 0, 0, str); waddstr(editWin, "> "); wattrset(editWin, A_NORMAL); if (buf->pos < buf->len) { waddnstr(editWin, buf->data, buf->pos); wattrset(editWin, A_REVERSE); waddch(editWin, buf->data[buf->pos]); wattrset(editWin, A_NORMAL); waddnstr(editWin, buf->data + buf->pos + 1, buf->len - buf->pos - 1); } else { waddnstr(editWin, buf->data, buf->len); wattrset(editWin, A_REVERSE); waddch(editWin, ' '); wattrset(editWin, A_NORMAL); } wrefresh(editWin); } int openConnection(struct in_addr *addr, int port) { struct sockaddr_in tmpAddr; int sock = -1; tmpAddr.sin_family = AF_INET; tmpAddr.sin_port = htons(port); tmpAddr.sin_addr = *addr; THMSG(1, "Connecting to %s:%d ...\n", inet_ntoa(tmpAddr.sin_addr), port); if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1) { THERR("Could not open socket: %s\n", strerror(errno)); return -2; } THMSG(2, "Using socket %d.\n", sock); if (connect(sock, (struct sockaddr *) &tmpAddr, sizeof(tmpAddr)) == -1) { THERR("Could not connect: %s\n", strerror(errno)); return -5; } return sock; } void closeConnection(int sock) { if (sock >= 0) { close(sock); } } BOOL sendToSocket(int sock, char *buf, const size_t bufLen) { size_t bufLeft = bufLen; char *bufPtr = buf; while (bufLeft > 0) { ssize_t bufSent; bufSent = send(sock, bufPtr, bufLeft, 0); if (bufSent < 0) return FALSE; bufLeft -= bufSent; bufPtr += bufSent; } return TRUE; } int printWin(WINDOW *win, const char *fmt) { const char *s = fmt; int col = 0; while (*s) { if (*s == '½') { int val = 0; s++; if (*s == '½') { waddch(win, *s | col); s++; } else { while (*s && isdigit(*s)) { val *= 10; val += (*s - '0'); s++; } if (*s != '½') return -1; s++; if (val < 9) { col = A_DIM | COLOR_PAIR(val); } else if (val < 30) { col = A_BOLD | COLOR_PAIR(val - 9); } } } else { waddch(win, *s | col); s++; } } return 0; } int printFile(FILE *outFile, const char *fmt) { const char *s = fmt; while (*s) { if (*s == '½') { s++; if (*s == '½') { s++; } else { while (*s && isdigit(*s)) s++; if (*s != '½') return -1; s++; } } else { fputc(*s, outFile); s++; } } return 0; } void printMsg(char *fmt, ...) { char tmpStr[128] = "", buf[8192]; va_list ap; time_t timeStamp; struct tm *tmpTime;; timeStamp = time(NULL); if ((tmpTime = localtime(&timeStamp)) != NULL) { strftime(tmpStr, sizeof(tmpStr), "½17½[½11½%H:%M:%S½17½]½0½ ", tmpTime); } va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (optLogFile) { printFile(optLogFile, tmpStr); printFile(optLogFile, buf); fflush(optLogFile); } if (!optDaemon) { printWin(mainWin, tmpStr); printWin(mainWin, buf); wrefresh(mainWin); } } BOOL bufRealloc(char **buf, size_t *size, size_t add) { return ((*buf = th_realloc(*buf, *size + add)) != NULL); } #define PUSHCHAR(x) bufPushChar(&result, &resSize, &resPos, x) BOOL bufPushChar(char **buf, size_t *size, size_t *pos, char ch) { if (*pos >= *size && !bufRealloc(buf, size, SET_ALLOC_SIZE)) return FALSE; (*buf)[*pos] = ch; (*pos)++; return TRUE; } #define PUSHSTR(x) bufPushStr(&result, &resSize, &resPos, x) BOOL bufPushStr(char **buf, size_t *size, size_t *pos, char *str) { size_t tmpLen; if (!str) return FALSE; tmpLen = strlen(str); if ((*pos + tmpLen) >= *size && !bufRealloc(buf, size, tmpLen + SET_ALLOC_SIZE)) return FALSE; strcpy(*buf + *pos, str); (*pos) += tmpLen; return TRUE; } typedef struct { char c; char *code; } conv_ent_t; conv_ent_t morseTab[] = { { ' ', " / " }, { 'A', ".-" }, { 'B', "-..." }, { 'C', "-.-." }, { 'D', "-.." }, { 'E', "." }, { 'F', "..-." }, { 'G', "--." }, { 'H', "...." }, { 'I', ".." }, { 'J', ".---" }, { 'K', "-.-" }, { 'L', ".-.." }, { 'M', "--" }, { 'N', "-." }, { 'O', "---" }, { 'P', ".--." }, { 'Q', "--.-" }, { 'R', ".-." }, { 'S', "..." }, { 'T', "-" }, { 'U', "..-" }, { 'V', "...-" }, { 'W', ".--" }, { 'X', "-..-" }, { 'Y', "-.--" }, { 'Z', "--.." }, { '0', "-----" }, { '1', ".----" }, { '2', "..---" }, { '3', "...--" }, { '4', "....-" }, { '5', "....." }, { '6', "-...." }, { '7', "--..." }, { '8', "---.." }, { '9', "----." }, { '.', ".-.-.-" }, { ',', "--..--" }, { '?', "..--.." }, }; char * conv2tab(conv_ent_t *tab, size_t ntab, char *str) { char *result, *s = str; size_t resSize, resPos = 0; if (!str) return NULL; resSize = (strlen(str) * 4) + SET_ALLOC_SIZE; if ((result = th_malloc(resSize)) == NULL) return NULL; while (*s) { size_t i; char c = toupper(*s); BOOL found = FALSE; for (i = 0; i < ntab; i++) if (tab[i].c == c) { PUSHSTR(tab[i].code); PUSHCHAR(' '); found = TRUE; break; } if (!found) PUSHCHAR(*s); s++; } PUSHCHAR(0); return result; } char *encodeStr1(char *str) { char *result, *s = str; size_t resSize, resPos = 0; if (!str) return NULL; resSize = strlen(str) + SET_ALLOC_SIZE; if ((result = th_malloc(resSize)) == NULL) return NULL; while (*s) { switch (*s) { case 32: PUSHCHAR('+'); break; default: if (th_isalnum(*s)) PUSHCHAR(*s); else { char tmpStr[4]; sprintf(tmpStr, "%2X", (unsigned char) *s); PUSHCHAR('%'); PUSHSTR(tmpStr); } break; } s++; } PUSHCHAR(0); return result; } int getxdigit(int c, int shift) { int i; if (c >= 'A' && c <= 'F') i = c - 'A' + 10; else if (c >= 'a' && c <= 'f') i = c - 'a' + 10; else if (c >= '0' && c <= '9') i = c - '0'; else return -1; return i << shift; } char *decodeStr1(char *str) { char *result, *s = str; size_t resSize, resPos = 0; int c; if (!str) return NULL; resSize = strlen(str) + SET_ALLOC_SIZE; if ((result = th_malloc(resSize)) == NULL) return NULL; while (*s) { switch (*s) { case '+': PUSHCHAR(' '); s++; break; case '½': /* Escape these .. */ PUSHCHAR('½'); PUSHCHAR('½'); s++; break; case '\r': PUSHCHAR(' '); s++; break; case '%': s++; if (*s == '%') PUSHCHAR('%'); else if ((c = getxdigit(*s, 4)) >= 0) { int i = getxdigit(*(++s), 0); if (i >= 0) { PUSHCHAR(c | i); } else { PUSHCHAR('§'); PUSHCHAR(*s); } } else { PUSHCHAR('§'); PUSHCHAR(*s); } s++; break; default: PUSHCHAR(*s); s++; } } PUSHCHAR(0); return result; } char *stripTags(char *str) { char *result, *s = str; size_t resSize, resPos = 0; if (!str) return NULL; resSize = strlen(str) + SET_ALLOC_SIZE; if ((result = th_malloc(resSize)) == NULL) return NULL; while (*s) { if (*s == '<') { while (*s && *s != '>') s++; if (*s == '>') s++; } else PUSHCHAR(*s++); } PUSHCHAR(0); return result; } typedef struct { char c; char *ent; } html_entity_t; html_entity_t HTMLEntities[] = { { '<', "<" }, { '>', ">" }, }; const int numHTMLEntities = (sizeof(HTMLEntities) / sizeof(HTMLEntities[0])); char *encodeStr2(char *str) { char *result, *s = str; size_t resSize, resPos = 0; if (!str) return NULL; resSize = strlen(str) + SET_ALLOC_SIZE; if ((result = th_malloc(resSize)) == NULL) return NULL; while (*s) { int i; BOOL found = FALSE; for (i = 0; i < numHTMLEntities; i++) if (HTMLEntities[i].c == *s) { PUSHSTR(HTMLEntities[i].ent); found = TRUE; break; } if (!found) PUSHCHAR(*s); s++; } PUSHCHAR(0); return result; } char *decodeStr2(char *str) { char *result, *s = str; size_t resSize, resPos = 0; if (!str) return NULL; resSize = strlen(str); if ((result = th_malloc(resSize)) == NULL) return NULL; while (*s) { if (*s == '&') { int i; BOOL found = FALSE; for (i = 0; i < numHTMLEntities; i++) { html_entity_t *ent = &HTMLEntities[i]; int len = strlen(ent->ent); if (!strncmp(s, ent->ent, len)) { PUSHCHAR(ent->c); s += len; found = TRUE; break; } } if (!found) PUSHCHAR(*s++); } else PUSHCHAR(*s++); } PUSHCHAR(0); return result; } BOOL sendUserMsg(int sock, char *user, char *fmt, ...) { char tmpBuf[4096], tmpBuf2[4096+256]; int n; va_list ap; va_start(ap, fmt); n = vsnprintf(tmpBuf, sizeof(tmpBuf), fmt, ap); va_end(ap); if (n < 0) return FALSE; snprintf(tmpBuf2, sizeof(tmpBuf2), "<USER>%s</USER><MESSAGE>%s</MESSAGE>", user, tmpBuf); return sendToSocket(sock, tmpBuf2, strlen(tmpBuf2) + 1); } int handleUser(int sock, char *str) { const char *msg = "</USER><MESSAGE>"; char *p = str, *q, *s, *t, *h; (void) sock; s = strstr(str, msg); if (!s) return 1; *s = 0; s += strlen(msg); q = strstr(s, "</MESSAGE>"); if (!q) return 3; *q = 0; s = decodeStr1(s); if (!s) return -1; p = decodeStr1(p); if (!p) { th_free(s); return -2; } if (*s == '/') { t = stripTags(s + 1); if (!strncmp(t, "BPRV", 4)) { h = decodeStr2(t + 1); printMsg("½11½%s½0½\n", h); } else { h = decodeStr2(t); printMsg("½9½* %s½0½\n", h); } th_free(h); th_free(t); } else { t = stripTags(s); h = decodeStr2(t); printMsg("½5½<½15½%s½5½>½0½ %s\n", p, h); th_free(h); th_free(t); } th_free(s); th_free(p); return 0; } int handleLogin(int sock, char *str) { char tmpStr[256] = ""; time_t timeStamp; struct tm *tmpTime;; timeStamp = time(NULL); if ((tmpTime = localtime(&timeStamp)) != NULL) { strftime(tmpStr, sizeof(tmpStr), "%c", tmpTime); } if (!strncmp(str, "FAILURE", 7)) { printMsg("½1½Login failure½0½ - ½3½%s½0½\n", tmpStr); return -2; } else if (!strncmp(str, "SUCCESS", 7)) { printMsg("½2½Login success½0½ - ½3½%s½0½\n", tmpStr); sendUserMsg(sock, optUserName2, "%%2FRequestUserList"); return 0; } else return 1; } int handleAddUser(int sock, char *str) { char *p, *s = strstr(str, "</ADD_USER>"); (void) sock; if (!s) return 1; *s = 0; p = decodeStr1(str); if (!p) return -1; printMsg("! ½3½%s½0½ ½2½ADDED.½0½\n", p); th_free(p); return 0; } int handleDeleteUser(int sock, char *str) { char *p, *s = strstr(str, "</DELETE_USER>"); (void) sock; if (!s) return 1; *s = 0; p = decodeStr1(str); if (!p) return -1; printMsg("! ½3½%s½0½ ½1½DELETED.½0½\n", p); th_free(p); return 0; } int handleFoo(int sock, char *str) { (void) sock; (void) str; return 0; } typedef struct { char *cmd; int (*handler)(int, char *); } protocmd_t; protocmd_t protoCmds[] = { { "<USER>", handleUser }, { "<LOGIN_", handleLogin }, { "<DELETE_USER>", handleDeleteUser }, { "<ADD_USER>", handleAddUser }, { "<NUMCLIENTS>", handleFoo }, }; const int nprotoCmds = (sizeof(protoCmds) / sizeof(protoCmds[0])); int handleProtocol(int sock, char *buf, size_t bufLen) { int i; for (i = 0; i < nprotoCmds; i++) { size_t cmdLen = strlen(protoCmds[i].cmd); if (cmdLen < bufLen && !strncmp(buf, protoCmds[i].cmd, cmdLen)) { return protoCmds[i].handler(sock, buf + cmdLen); } } return 1; } int handleUserInput(int sock, char *buf, size_t bufLen) { char *tmpStr, *tmpStr2, tmpBuf[4096]; BOOL result; /* Trim right */ buf[--bufLen] = 0; while (bufLen > 0 && (buf[bufLen] == '\n' || buf[bufLen] == '\r' || th_isspace(buf[bufLen]))) buf[bufLen--] = 0; /* Check command */ if (*buf == 0) { return 1; } else if (!strncmp(buf, "/color ", 7)) { int tmpInt; if ((tmpInt = getColor(buf+7)) < 0) { printMsg("Invalid color value '%s'\n", buf+7); return 1; } optUserColor = tmpInt; printMsg("Setting color to #%06x\n", optUserColor); sendUserMsg(sock, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); return 0; } else if (!strncmp(buf, "/flood ", 7)) { int i; snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg . .", buf+7); tmpStr = encodeStr2(tmpBuf); if (!tmpStr) return -2; tmpStr2 = encodeStr1(tmpStr); if (!tmpStr2) { th_free(tmpStr); return -3; } result = TRUE; for (i = 0; i < 50 && result; i++) { result = sendUserMsg(sock, optUserName2, "%s", tmpStr2); usleep(250); } th_free(tmpStr); th_free(tmpStr2); return 0; } else if (!strncmp(buf, "/to ", 4)) { th_free(setTarget); setTarget = th_strdup(buf + 4); printMsg("Set prv target to '%s'\n", setTarget); return 0; } else if (setPrvMode) { if (setTarget != NULL) { snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg %s", setTarget, buf); buf = tmpBuf; } else { printMsg("No target set, exiting prv mode.\n"); setPrvMode = FALSE; return 1; } } else if (buf[0] != '/') { if (setMorseMode) { tmpStr = conv2tab(morseTab, sizeof(morseTab) / sizeof(morseTab[0]), buf); strncpy(tmpBuf, tmpStr, sizeof(tmpBuf)); tmpBuf[sizeof(tmpBuf)-1] = 0; th_free(tmpStr); buf = tmpBuf; } } /* Send double-encoded */ tmpStr = encodeStr2(buf); if (!tmpStr) return -2; tmpStr2 = encodeStr1(tmpStr); if (!tmpStr2) { th_free(tmpStr); return -3; } result = sendUserMsg(sock, optUserName2, "%s", tmpStr2); th_free(tmpStr); th_free(tmpStr2); if (result) return 0; else return -1; } int main(int argc, char *argv[]) { int tmpSocket, curVis, updateCount = 0; struct hostent *tmpHost; BOOL argsOK, isError = FALSE, exitProg = FALSE, colorSet = FALSE, cursesInit = FALSE, insertMode = TRUE; struct timeval tv; fd_set sockfds; char *tmpStr; editbuf_t *editBuf = newBuf(SET_BUFSIZE); editbuf_t *histBuf[SET_MAX_HISTORY+2]; int histPos = 0, histMax = 0; memset(histBuf, 0, sizeof(histBuf)); /* Initialize */ th_init("NNChat", "Newbie Nudes chat client", "0.6.2", "Written and designed by Anonymous Finnish Guy (C) 2008", "This software is freeware, use and distribute as you wish."); th_verbosityLevel = 0; /* Parse arguments */ argsOK = th_args_process(argc, argv, optList, optListN, argHandleOpt, argHandleFile, FALSE); /* Check the mode and arguments */ if (optUserName == NULL || optPassword == NULL) { THERR("User/pass not specified, get some --help\n"); return -1; } if (!argsOK) return -2; /* Open logfile */ if (optLogFilename) { THMSG(1, "Opening logfile '%s'\n", optLogFilename); if ((optLogFile = fopen(optLogFilename, "a")) == NULL) { THERR("Could not open logfile for appending!\n"); return -9; } } /* Okay ... */ THMSG(1, "Trying to resolve host '%s' ...\n", optServer); tmpHost = gethostbyname(optServer); if (tmpHost == NULL) { THERR("Could not resolve hostname: %s.\n", hstrerror(h_errno)); return -3; } THMSG(2, "True hostname: %s\n", tmpHost->h_name); #if 0 /* To emulate the official client, we first make a fake connection ... */ if ((tmpSocket = openConnection((struct in_addr *) tmpHost->h_addr, optPort)) < 0) { THERR("Fakeprobe connection setup failed!\n"); goto err_exit; } tmpStr = "<policy-file-request/>"; if (sendToSocket(tmpSocket, tmpStr, strlen(tmpStr) + 1) == FALSE) { THERR("Failed to send fakeprobe.\n"); goto err_exit; } else { ssize_t gotBuf; char tmpBuf[4096]; gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0); tmpBuf[gotBuf-1] = 0; THMSG(2, "Probe got: %s\n", tmpBuf); closeConnection(tmpSocket); } #endif /* Okay, now do the proper connection ... */ if ((tmpSocket = openConnection((struct in_addr *) tmpHost->h_addr, optPort)) < 0) { THERR("Main connection setup failed!\n"); goto err_exit; } THMSG(1, "Connected, logging in as '%s', site '%s'.\n", optUserName, optSite); optUserName2 = encodeStr1(optUserName); tmpStr = encodeStr1(optSite); sendUserMsg(tmpSocket, optUserName2, "%%2Flogin%%20%%2Dsite%%20%s%%20%%2Dpassword%%20%s", tmpStr, optPassword); th_free(tmpStr); /* Initialize NCurses */ if (!optDaemon) { initscr(); raw(); keypad(stdscr, TRUE); noecho(); timeout(SET_DELAY); curVis = curs_set(0); if (has_colors()) { start_color(); init_pair(1, COLOR_RED, COLOR_BLACK); init_pair(2, COLOR_GREEN, COLOR_BLACK); init_pair(3, COLOR_YELLOW, COLOR_BLACK); init_pair(4, COLOR_BLUE, COLOR_BLACK); init_pair(5, COLOR_MAGENTA, COLOR_BLACK); init_pair(6, COLOR_CYAN, COLOR_BLACK); init_pair(7, COLOR_WHITE, COLOR_BLACK); init_pair(8, COLOR_BLACK, COLOR_BLACK); init_pair(10, COLOR_BLACK, COLOR_RED); init_pair(11, COLOR_WHITE, COLOR_RED); init_pair(12, COLOR_GREEN, COLOR_RED); init_pair(13, COLOR_YELLOW, COLOR_RED); init_pair(14, COLOR_BLUE, COLOR_RED); init_pair(15, COLOR_MAGENTA, COLOR_RED); init_pair(16, COLOR_CYAN, COLOR_RED); } mainWin = newwin(LINES - 4, COLS, 0, 0); statusWin = newwin(1, COLS, LINES - 4, 0); editWin = newwin(3, COLS, LINES - 3, 0); if (mainWin == NULL || statusWin == NULL || editWin == NULL) { THERR("Could not create ncurses windows!\n"); goto err_exit; } scrollok(mainWin, 1); clearBuf(editBuf); printEditBuf("", editBuf); updateStatus(insertMode); cursesInit = TRUE; } /* Enter mainloop */ FD_ZERO(&sockfds); FD_SET(tmpSocket, &sockfds); while (!isError && !exitProg) { int result; fd_set tmpfds; /* Check for incoming data from the server */ tv.tv_sec = 0; tv.tv_usec = SET_DELAY_USEC; tmpfds = sockfds; if ((result = select(tmpSocket+1, &tmpfds, NULL, NULL, &tv)) == -1) { printMsg("Error occured in select(sockfds): %s\n", strerror(errno)); isError = TRUE; } else if (FD_ISSET(tmpSocket, &tmpfds)) { ssize_t gotBuf; char tmpBuf[8192]; gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0); if (gotBuf < 0) { printMsg("Error in recv: %s\n", strerror(errno)); isError = TRUE; } else if (gotBuf == 0) { printMsg("Server closed connection.\n"); isError = TRUE; } else { /* Handle protocol data */ tmpBuf[gotBuf] = 0; result = handleProtocol(tmpSocket, tmpBuf, gotBuf); if (result > 0) { /* Couldn't handle the message for some reason */ printMsg("Could not handle: %s\n", tmpBuf); } else if (result < 0) { /* Fatal error, quit */ printMsg("Fatal error with message: %s\n", tmpBuf); isError = TRUE; } updateStatus(insertMode); } } /* Handle user input */ if (!optDaemon) { int c, cnt = 0; BOOL update = FALSE; /* Handle several buffered keypresses at once */ do { c = getch(); switch (c) { case KEY_ENTER: case '\n': case '\r': /* Call the user input handler */ if (editBuf->len > 0) { if (histMax > 0) { freeBuf(histBuf[SET_MAX_HISTORY+1]); histBuf[SET_MAX_HISTORY+1] = NULL; memmove(&histBuf[2], &histBuf[1], histMax * sizeof(histBuf[0])); } histPos = 0; histBuf[1] = copyBuf(editBuf); if (histMax < SET_MAX_HISTORY) histMax++; insertBuf(editBuf, editBuf->len, 0); result = handleUserInput(tmpSocket, editBuf->data, editBuf->len); clearBuf(editBuf); if (result < 0) { printMsg("Fatal error handling user input: %s\n", editBuf->data); isError = TRUE; } update = TRUE; } break; case 0x09: /* Tab = switch between PRV */ if (setPrvMode) setPrvMode = FALSE; else { if (setTarget != NULL) setPrvMode = TRUE; } update = TRUE; break; case KEY_UP: if (histPos == 0) { freeBuf(histBuf[0]); histBuf[0] = copyBuf(editBuf); } if (histPos < histMax) { histPos++; freeBuf(editBuf); editBuf = copyBuf(histBuf[histPos]); update = TRUE; } break; case KEY_DOWN: if (histPos > 0) { histPos--; freeBuf(editBuf); editBuf = copyBuf(histBuf[histPos]); update = TRUE; } break; case 0x204: /* ctrl+left */ while (editBuf->pos > 0 && isspace(editBuf->data[editBuf->pos - 1])) editBuf->pos--; while (editBuf->pos > 0 && !isspace(editBuf->data[editBuf->pos - 1])) editBuf->pos--; update = TRUE; break; case 0x206: /* ctrl+right */ while (editBuf->pos < editBuf->len && isspace(editBuf->data[editBuf->pos])) editBuf->pos++; while (editBuf->pos < editBuf->len && !isspace(editBuf->data[editBuf->pos])) editBuf->pos++; if (editBuf->pos > editBuf->len) editBuf->pos = editBuf->len; update = TRUE; break; case 0x111: /* F9 */ printMsg("Quitting per user request.\n"); exitProg = TRUE; break; case 0x109: /* F1 */ insertMode = !insertMode; update = TRUE; break; case 0x10a: /* F2 */ clearBuf(editBuf); update = TRUE; break; case 0x10b: /* F3 */ setMorseMode = !setMorseMode; printMsg("Morse code mode = %d\n", setMorseMode); update = TRUE; break; case KEY_HOME: setBufPos(editBuf, 0); update = TRUE; break; case KEY_END: setBufPos(editBuf, editBuf->len); update = TRUE; break; case KEY_LEFT: setBufPos(editBuf, editBuf->pos - 1); update = TRUE; break; case KEY_RIGHT: setBufPos(editBuf, editBuf->pos + 1); update = TRUE; break; case KEY_BACKSPACE: deleteBuf(editBuf, editBuf->pos - 1); setBufPos(editBuf, editBuf->pos - 1); update = TRUE; break; case 0x14a: /* Delete */ deleteBuf(editBuf, editBuf->pos); update = TRUE; break; case 0x0c: /* ctrl+l */ redrawwin(mainWin); redrawwin(statusWin); redrawwin(editWin); break; case ERR: /* Ignore */ break; default: if (isprint(c) || c == 0xe4 || c == 0xf6 || c == 0xc4 || c == 0xd6) { if (insertMode) insertBuf(editBuf, editBuf->pos, c); else writeBuf(editBuf, editBuf->pos, c); setBufPos(editBuf, editBuf->pos + 1); update = TRUE; } else { printMsg("Unhandled key: %02x\n", c); } break; } } while (c != ERR && !exitProg && ++cnt < 10); if (update) { /* Update edit line */ printEditBuf(setPrvMode ? setTarget : "", editBuf); updateStatus(insertMode); } } /* !optDaemon */ if (++updateCount > 10) { updateStatus(insertMode); updateCount = 0; } if (!colorSet) { colorSet = TRUE; printMsg("%s v%s - %s\n", th_prog_name, th_prog_version, th_prog_fullname); printMsg("%s\n", th_prog_author); printMsg("%s\n", th_prog_license); sendUserMsg(tmpSocket, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); } } /* Shutdown */ err_exit: if (cursesInit) { if (curVis != ERR) curs_set(curVis); endwin(); THMSG(1, "NCurses deinitialized.\n"); } if (isError) { THMSG(1, "Error exit.\n"); } th_free(optUserName2); closeConnection(tmpSocket); THMSG(1, "Connection terminated.\n"); if (optLogFile) { THMSG(1, "Closing logfile.\n"); fclose(optLogFile); } return 0; }