view nnchat.c @ 27:da721f94c60f

Use ANSI colours.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 02 Aug 2008 04:04:58 +0300
parents b84fc46c6035
children 512775f6b081
line wrap: on
line source

#ifdef __WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <netdb.h>
#endif

#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>


#define SET_ALLOC_SIZE	(128)
#define SET_SELECT_USEC (100000)

#define ANSI_BLACK      "\x1b[0;30m"
#define ANSI_RED        "\x1b[0;31m"
#define ANSI_GREEN      "\x1b[0;32m"
#define ANSI_YELLOW     "\x1b[0;33m"
#define ANSI_BLUE       "\x1b[0;34m"
#define ANSI_MAGENTA    "\x1b[0;35m"
#define ANSI_CYAN       "\x1b[0;36m"
#define ANSI_WHITE      "\x1b[0;37m"

#define ANSI_L_BLACK    "\x1b[0;1;30m"
#define ANSI_L_RED      "\x1b[0;1;31m"
#define ANSI_L_GREEN    "\x1b[0;1;32m"
#define ANSI_L_YELLOW   "\x1b[0;1;33m"
#define ANSI_L_BLUE     "\x1b[0;1;34m"
#define ANSI_L_MAGENTA  "\x1b[0;1;35m"
#define ANSI_L_CYAN     "\x1b[0;1;36m"
#define ANSI_L_WHITE    "\x1b[0;1;37m"

#define ANSI_END        "\x1b[0m"


/* Options
 */
int     optPort = 8005;
int     optUserColor = 0x408060;
char    *optServer = "www11.servemedata.com",
		*optUserName = NULL,
		*optUserName2 = NULL,
		*optPassword = NULL,
		*optLogFilename = NULL,
		*setTarget = NULL;
BOOL	optDaemon = FALSE;
FILE	*optLogFile = NULL;


/* 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 },
};

const int optListN = (sizeof(optList) / sizeof(optarg_t));


void argShowHelp()
{
	th_args_help(stdout, optList, optListN, th_prog_name,
		"[options] <username> <password>");
}


#ifdef __WIN32
/* Just a bogus stub
 */
const char *hstrerror(int err)
{
	(void) err;

	return "???";
}
#endif


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 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;
}


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) {
#ifdef __WIN32
		closesocket(sock);
#else
		close(sock);
#endif
	}
}


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;
}


void printMsg(char *fmt, char *fmt2, ...)
{
	char tmpStr[64] = "";
	va_list ap;
	time_t timeStamp;
	struct tm *tmpTime;;
	
	timeStamp = time(NULL);
	if ((tmpTime = localtime(&timeStamp)) != NULL) {
		strftime(tmpStr, sizeof(tmpStr), "%H:%M:%S", tmpTime);
	}
	
	if (optLogFile) {
		fprintf(optLogFile, "[%s] ", tmpStr);
		va_start(ap, fmt2);
		vfprintf(optLogFile, fmt, ap);
		va_end(ap);
		fflush(optLogFile);
	}
	
	if (!optDaemon) {
		fprintf(stdout, ANSI_L_BLACK "[" ANSI_L_GREEN "%s" ANSI_L_BLACK "]" ANSI_END " ", tmpStr);
		va_start(ap, fmt2);
		vfprintf(stdout, fmt2, ap);
		va_end(ap);
		fflush(stdout);
	}
}


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;
}


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 '\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[] = {
	{ '<', "&lt;" },
	{ '>', "&gt;" },
};

const int numHTMLEntities = (sizeof(HTMLEntities) / sizeof(html_entity_t));


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 == '/') {
		if (!strncmp(s, "/BPRV", 5)) {
			t = stripTags(s + 2);
			h = decodeStr2(t);
			printMsg("%s\n", ANSI_YELLOW "%s" ANSI_END  "\n", h);
		} else {
			t = stripTags(s + 1);
			h = decodeStr2(t);
			printMsg("* %s\n", ANSI_L_YELLOW "* %s" ANSI_END "\n", h);
		}
		th_free(h);
		th_free(t);
	} else {
		t = stripTags(s);
		h = decodeStr2(t);
		printMsg("<%s> %s\n", ANSI_MAGENTA "<" ANSI_L_CYAN "%s" ANSI_MAGENTA ">" ANSI_END " %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("Login failure - %s\n", "Login failure - %s\n", tmpStr);
		return -2;
	} else if (!strncmp(str, "SUCCESS", 7)) {
		printMsg("Login success - %s\n", "Login success - %s\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("! %s ADDED.\n", "! " ANSI_GREEN "%s" ANSI_END " ADDED.\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("! %s DELETED.\n", "! " ANSI_RED "%s" ANSI_END " DELETED.\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(protocmd_t));


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;
	
	//fprintf(stderr, "'%s'\n", buf); fflush(stderr);
	
	/* Check command */
	if (*buf == 0) {
		return 1;
	} else if (!strncmp(buf, "/color ", 7)) {
		if ((optUserColor = getColor(buf+7)) < 0) {
			printMsg("Invalid color value '%s'\n", "Invalid color value '%s'\n", buf+7);
			return 1;
		}
		printMsg("Setting color to #%06x\n", "Setting color to #%06x\n", optUserColor);
		sendUserMsg(sock, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor);
		return 0;
	} else if (!strncmp(buf, "/fake ", 6)) {
		printMsg("Sending /%s\n", "Sending /%s\n", buf+6);
		tmpStr = encodeStr2(tmpBuf);
		if (!tmpStr) return -2;
		tmpStr2 = encodeStr1(tmpStr);
		if (!tmpStr2) {
			th_free(tmpStr);
			return -3;
		}
		sendUserMsg(sock ,optUserName2, "%%2F%s", tmpStr2);
		
		th_free(tmpStr);
		th_free(tmpStr2);
		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, "/msg ", 5)) {
		if (setTarget) {
			snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg %s", setTarget, buf+5);
			buf = tmpBuf;
		} else {
			printMsg("No target set!\n", ANSI_L_RED "No target set!" ANSI_END "\n");
			return 1;
		}
	} else if (!strncmp(buf, "/to ", 4)) {
		buf += 4;
		th_free(setTarget);
		setTarget = th_strdup(buf);
		printMsg("Set prv target to '%s'\n",
			"Set prv target to '" ANSI_L_GREEN "%s" ANSI_END "'\n", setTarget);
		return 0;
	}
	
	{
		/* Send double-encoded */
		//printf("ENC>%s\n", buf);
		//fflush(stdout);
	
		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;
	struct hostent *tmpHost;
	BOOL argsOK, exitProg = FALSE, colorSet = FALSE;
	struct timeval tv;
	fd_set sockfds;
	fd_set inputfds;
	char *tmpStr;

	/* Initialize */
	th_init("NNChat", "Newbie Nudes chat client", "0.4",
		"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;
		}
	}
	
#ifdef __WIN32
	{
		WSADATA wsaData;
		if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
			THERR("WinSock API v2.0 not supported.\n");
			return -20;
		}
	}
#endif

	/* 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);

	/* 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);
	}

	/* 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'.\n", optUserName);
	optUserName2 = encodeStr1(optUserName);
	
	sendUserMsg(tmpSocket, optUserName2, "%%2Flogin%%20%%2Dsite%%20NN%%20%%2Dpassword%%20%s", optPassword);
	
	FD_ZERO(&inputfds);
	FD_SET(0, &inputfds);
	FD_ZERO(&sockfds);
	FD_SET(tmpSocket, &sockfds);

	while (!exitProg) {
		ssize_t gotBuf;
		int result;
		char tmpBuf[4096];
		fd_set tmpfds;
		
		/* Check for incoming data from the server */
		tv.tv_sec = 0;
		tv.tv_usec = SET_SELECT_USEC;
		tmpfds = sockfds;
		if ((result = select(tmpSocket+1, &tmpfds, NULL, NULL, &tv)) == -1) {
			printMsg("Error occured in select(sockfds): %s\n",
				"Error occured in select(sockfds): %s\n",
				strerror(errno));
			exitProg = TRUE;
		} else if (FD_ISSET(tmpSocket, &tmpfds)) {
			gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0);
			
			if (gotBuf < 0) {
				printMsg("Error in recv: %s\n", strerror(errno));
				exitProg = TRUE;
			} else if (gotBuf == 0) {
				printMsg("Server closed connection.\n", "Server closed connection.\n");
				exitProg = 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", "Could not handle: %s\n", tmpBuf);
				} else if (result < 0) {
					/* Fatal error, quit */
					printMsg("Fatal error with message: %s\n", tmpBuf);
					exitProg = TRUE;
				}
			}
		}
		
		/* Check for user input */
		if (!optDaemon) {
		tv.tv_sec = 0;
		tv.tv_usec = SET_SELECT_USEC;
		tmpfds = inputfds;
		if ((result = select(1, &tmpfds, NULL, NULL, &tv)) == -1) {
			printMsg("Error occured in select(inputfds): %s\n", strerror(errno));
			exitProg = TRUE;
		} else if (FD_ISSET(0, &tmpfds)) {
			gotBuf = read(0, tmpBuf, sizeof(tmpBuf));
			
			if (gotBuf < 0) {
				printMsg("Error in reading stdio.\n", "Error in reading stdio.\n");
				exitProg = TRUE;
			} else {
				/* Call the user input handler */
				result = handleUserInput(tmpSocket, tmpBuf, gotBuf);
				if (result < 0) {
					printMsg("Fatal error handling user input: %s\n",
						tmpBuf);
					exitProg = TRUE;
				}
			}
		}
		} /* !optDaemon */
		
		if (!colorSet) {
			colorSet = TRUE;
			sendUserMsg(tmpSocket, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor);
		}
		
		fflush(stdout);
		fflush(stderr);
	}
	
	/* Shotdiwn */
err_exit:
	THMSG(1, "Error exit.\n");
	th_free(optUserName2);

	closeConnection(tmpSocket);
	
#ifdef __WIN32
	WSACleanup();
#endif

	THMSG(1, "Connection terminated.\n");
	
	if (optLogFile) {
		THMSG(1, "Closing logfile.\n");
		fclose(optLogFile);
	}
	
	
	return 0;
}