roh/conf/area/
roh/game/talk/
roh/help/
roh/monsters/ocean/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.44b/
/*
 * socket.c
 *   Stuff to deal with sockets
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
// C Includes
#include <fcntl.h>		// Needs: fnctl
#include <netdb.h>		// Needs: gethostbyaddr
#include <sys/socket.h>	// Needs: AF_INET
#include <arpa/telnet.h>// IAC
#include <errno.h>

// C++ Includes
#include <sstream>

// Mud Includes
#include "mud.h"
#include "login.h"
#include "commands.h"
#include "property.h"
#include "version.h"


// Static initilization
const int Socket::COMPRESSED_OUTBUF_SIZE = 8192;
int Socket::NumSockets = 0;

//********************************************************************
//						telnet namespace
//********************************************************************

namespace telnet {
	// MXP Support
	unsigned const char will_mxp[] = { IAC, WILL, TELOPT_MXP, '\0' };
	// MCCP V2 support
	unsigned const char will_comp2[] = { IAC, WILL, TELOPT_COMPRESS2, '\0' };
	// MCCP V1 support
	unsigned const char will_comp1[] = { IAC, WILL, TELOPT_COMPRESS, '\0' };
	// Echo input
	unsigned const char will_echo[] = { IAC, WILL, TELOPT_ECHO, '\0' };
	// EOR After every prompt
	unsigned const char will_eor[] = { IAC, WILL, TELOPT_EOR, '\0'};
	// MSSP Support
	unsigned const char will_mssp[] = { IAC, WILL, TELOPT_MSSP, '\0'};
	// MSSP SB
	unsigned const char sb_mssp_start[] = {IAC, SB, TELOPT_MSSP, '\0'};
	// MSSP SB stop
	unsigned const char sb_mssp_end[] = {IAC, SE, '\0'};
	// Terminal type negotation
	unsigned const char do_ttype[] = { IAC, DO, TELOPT_TTYPE, '\0' };
	// Window size negotation NAWS
	unsigned const char do_naws[] = { IAC, DO, TELOPT_NAWS, '\0' };
	// Start mxp string
	unsigned const char start_mxp[] = { IAC, SB, TELOPT_MXP, IAC, SE, '\0' };
	// Start string for compress2
	unsigned const char start_mccp2[] = { IAC, SB, TELOPT_COMPRESS2, IAC, SE, '\0' };
	// start string for compress1
	unsigned const char start_mccp[] = { IAC, SB, TELOPT_COMPRESS, WILL, SE, '\0' };
	// Start sub negotiation for terminal type
	unsigned const char query_ttype[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE, '\0' };

	// End of line string
	unsigned const char eor_str[] = {IAC, EOR, '\0' };

	// MCCP Hooks
	void *zlib_alloc(void *opaque, unsigned int items, unsigned int size) {
		return calloc(items, size);
	}
	void zlib_free(void *opaque, void *address) {
		free(address);
	}
}


//--------------------------------------------------------------------
// Constructors, Destructors, etc

//********************************************************************
//						reset
//********************************************************************

void Socket::reset() {
	fd = 0;
	opts.mccp = 0;
	opts.mxp = false;
	opts.eor = false;
	opts.compressing = false;
	inPlayerList = false;

	out_compress_buf = NULL;
	out_compress = NULL;
	myPlayer = NULL;

	tState = NEG_NONE;
	oneIAC = watchBrokenClient = false;


	term.type = "";
	term.cols = 82;
	term.rows = 40;

	color = 0;
	ltime = time(0);
	intrpt = 0;

	fn = 0;
	timeout = 0;
	ansi = 0;
	tState = NEG_NONE;
	connState = LOGIN_START;
	lastState = LOGIN_START;

	zero(tempstr, sizeof(tempstr));
	inBuf = "";
	spyingOn = 0;
}

//********************************************************************
//						Socket
//********************************************************************

Socket::Socket(int pFd) {
	reset();
	fd = pFd;
	sendTelnetNeg();
	NumSockets++;
}

Socket::Socket(int pFd, sockaddr_in pAddr, bool &dnsDone) {
	reset();

	struct linger ling;
	fd = pFd;

	resolveIp(pAddr, host.ip);
	resolveIp(pAddr, host.hostName); // Start off with the hostname as the ip, then do
									 // an asyncronous lookup

// Make this socket non blocking
	nonBlock(fd);

// Set Linger behavior
	ling.l_onoff = ling.l_linger = 0;
	setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(struct linger));

	NumSockets++;
	std::cout << "Constructing socket (" << fd << ") from " << host.ip
		<< " Socket #" << NumSockets << std::endl;

	// If we're running under valgrind, we don't resolve dns.  The child
	// process tends to mess with proper memory leak detection
	if(gServer->getDnsCache(host.ip, host.hostName) || gServer->isValgrind()) {
		dnsDone = true;
	} else {
		dnsDone = false;
		setState(LOGIN_DNS_LOOKUP);
		gServer->startDnsLookup(this, pAddr);
	}

	sendTelnetNeg();
}

//********************************************************************
//						~Socket
//********************************************************************

Socket::~Socket() {
	std::cout << "Deconstructing socket with fd of " << fd << ", ";
	NumSockets--;
	std::cout << "Num sockets: " << NumSockets << std::endl;

	clearSpying();
	clearSpiedOn();

	if(myPlayer) {
		if(myPlayer->fd > -1) {
			myPlayer->save(true);
			myPlayer->uninit();
		}
		freePlayer();
	}

	endCompress();
	close(fd);
}

//********************************************************************
//						addToPlayerList
//********************************************************************

void Socket::addToPlayerList() { inPlayerList = true; }

//********************************************************************
//						freePlayer
//********************************************************************

void Socket::freePlayer() {
	if(myPlayer)
		free_crt(myPlayer, inPlayerList);
	myPlayer = 0;
	inPlayerList = false;
}

// End - Constructors, Destructors, etc
//--------------------------------------------------------------------

//********************************************************************
//						clearSpying
//********************************************************************

void Socket::clearSpying() {
	if(spyingOn) {
		spyingOn->removeSpy(this);
		spyingOn = 0;
	}
}

//********************************************************************
//						clearSpiedOn
//********************************************************************

void Socket::clearSpiedOn() {
	std::list<Socket*>::iterator it;
	for(it = spying.begin() ; it != spying.end() ; it++ ) {
		Socket *sock = *it;
		if(sock)
			sock->setSpying(NULL);
	}
	spying.clear();
}

//********************************************************************
//						setSpying
//********************************************************************

void Socket::setSpying(Socket *sock) {
	if(sock)
		clearSpying();
	spyingOn = sock;

	if(sock)
		sock->addSpy(this);
	if(!sock) {
		if(myPlayer)
			myPlayer->clearFlag(P_SPYING);
	}
}

//********************************************************************
//						removeSpy
//********************************************************************

void Socket::removeSpy(Socket *sock) {
	spying.remove(sock);
	if(myPlayer->getClass() >= sock->myPlayer->getClass())
		sock->printColor("^r%s is no longer observing you.\n", sock->myPlayer->name);
}

//********************************************************************
//						addSpy
//********************************************************************

void Socket::addSpy(Socket *sock) {
	spying.push_back(sock);
}

//********************************************************************
//						disconnect
//********************************************************************
// Set this socket for removal on the next cleanup

void Socket::disconnect() {
	// TODO: Perhaps remove player from the world right here.
	setState(CON_DISCONNECTING);
}

//********************************************************************
//						resolveIp
//********************************************************************

void Socket::resolveIp(const sockaddr_in &addr, bstring& ip) {
	std::ostringstream tmp;
	long i = htonl(addr.sin_addr.s_addr);
	tmp << ((i >> 24) & 0xff) << "." << ((i >> 16) & 0xff) << "." << ((i >> 8) & 0xff) << "." << (i & 0xff);
	ip = tmp.str();
}

//********************************************************************
//						checkLockOut
//********************************************************************

void Socket::checkLockOut() {
	int lockStatus = gConfig->isLockedOut(this);
	if(lockStatus == 0) {
		print("\n\nChoose - [A]nsi color, [M]irc color, [X] MXP + Ansi, or [N]o color\n: ");
		setState(LOGIN_GET_COLOR);
	}
	else if(lockStatus == 2) {
		print("\n\nA password is required to play from your site: ");
		setState(LOGIN_GET_LOCKOUT_PASSWORD);
	}
	else if(lockStatus == 1) {
		print("\n\nYou are not wanted here. Begone.\n");
		setState(CON_DISCONNECTING);
	}
}

//********************************************************************
//						processInput
//********************************************************************

int Socket::processInput() {
	unsigned char tmpBuf[1024];
	int n;
	unsigned int i = 0;
	bstring tmp = "";

	// Attempt to read from the socket
	n = read(getFd(), tmpBuf, 1023);
	if(n <= 0)
		if(errno != EWOULDBLOCK)
			return(-1);

	tmp.reserve(n);

	InBytes += n;
// TODO:  Put timeout code back in O:-)

	tmpBuf[n] = '\0';
	// If we have any full strings, copy it over to the queue to be interpreted

	// Look for any IAC commands using a finite state machine
	for(i=0; i < (unsigned)n; i++) {

// For debugging
//		std::cout << "TELOPT:" << (unsigned int)tmpBuf[i] << "\n";


		// Try to handle zMud, cMud & tintin++ which don't seem to double the IAC for NAWS
		if(oneIAC && tState > NEG_START_NAWS && tState < NEG_END_NAWS && tmpBuf[i] != IAC) {
			// Broken Client
			std::cout << "NAWS: BUG - Broken Client: Non-doubled IAC\n";
			i--;
		}
		if(watchBrokenClient) {
			// If we just finished NAWS with a 255 height...keep an eye out for the next
			// character to be a stray SE
			if(tState == NEG_NONE && (unsigned char)tmpBuf[i] == SE) {
				std::cout << "NAWS: BUG - Stray SE\n";
				// Set the tState to NEG_IAC as it should have been, and carry gracefully on
				tState = NEG_IAC;
			}
			// It should only be the next character, so if we don't find it...don't keep looking for it
			watchBrokenClient = false;
		}

		switch(tState) {
		case NEG_NONE:
			// Expecting an IAC here
			if((unsigned char)tmpBuf[i] == IAC) {
				tState = NEG_IAC;
				continue;
			} else {
				tmp += tmpBuf[i];
				break;
			}
			break;
		case NEG_IAC:
			switch((unsigned char)tmpBuf[i]) {
				case NOP:
				case IP:
				case GA: tState = NEG_NONE; break;
				case WILL: tState = NEG_WILL; break;
				case WONT: tState = NEG_WONT; break;
				case DO: tState = NEG_DO; break;
				case DONT: tState = NEG_DONT; break;
				case SE: tState = NEG_NONE; break;
				case SB: tState = NEG_SB; break;
				default:
					tState = NEG_NONE;
					break;
			}
			break;
			// Handle Do and Will
		case NEG_DO:
		case NEG_WILL:
			switch((unsigned char)tmpBuf[i]) {
				case TELOPT_MXP:
					write(telnet::start_mxp);
					//TODO: send elements we're using for mxp
					opts.mxp = true;
					tState = NEG_NONE;
					break;
				case TELOPT_COMPRESS2:
					opts.mccp = 2;
					startCompress();
					tState = NEG_NONE;
					break;
				case TELOPT_COMPRESS:
					opts.mccp = 1;
					startCompress();
					tState = NEG_NONE;
					break;
				case TELOPT_TTYPE:
					write(telnet::query_ttype);
					tState = NEG_NONE;
					break;
				case TELOPT_EOR:
					opts.eor = TRUE;
					printf("Activating EOR\n");
					tState = NEG_NONE;
					break;
				case TELOPT_ECHO:
				case TELOPT_NAWS:
				case TELOPT_NEW_ENVIRON:
					// TODO: Echo/naws/New Environ
					tState = NEG_NONE;
					break;
				case TELOPT_MSSP:
					sendMSSP();
					tState = NEG_NONE;
					break;
				default:
					tState = NEG_NONE;
					break;
			}
			break;
			// Handle Dont and Wont
		case NEG_DONT:
		case NEG_WONT:
			switch((unsigned char)tmpBuf[i]) {
			case TELOPT_MXP:
				opts.mxp = FALSE;
				tState = NEG_NONE;
				break;
			case TELOPT_COMPRESS2:
				if(opts.mccp == 2) {
					opts.mccp = 0;
					endCompress();
				}
				tState = NEG_NONE;
				break;
			case TELOPT_COMPRESS:
				if(opts.mccp == 1) {
					opts.mccp = 0;
					endCompress();
				}
				tState = NEG_NONE;
				break;
			case TELOPT_EOR:
				opts.eor = FALSE;
				printf("Deactivating EOR\n");
				tState = NEG_NONE;
				break;
			case TELOPT_ECHO:
				// TODO Echo negotiation
				tState = NEG_NONE;
				break;
			default:
				tState = NEG_NONE;
				break;
			}
			break;
		case NEG_SB:
			switch((unsigned char)tmpBuf[i]) {
				case NAWS:
					tState = NEG_SB_NAWS_COL_HIGH;
					break;
				case TTYPE:
					tState = NEG_SB_TTYPE;
					break;
				default:
					std::cout << "Unknown Sub Negotiation" << std::endl;
					tState = NEG_NONE;
					break;
			}
			break;
		case NEG_SB_TTYPE:
			// Grab the terminal type
			if(tmpBuf[i] == TELQUAL_IS) {
				i++;
			}
			term.type.erase();
			term.type = "";
			while(tmpBuf[i]) {
				term.type += tmpBuf[i];
				if((unsigned char)tmpBuf[i+1] == IAC)
					break;
				else
					i++;
			}

			std::cout << "Found term type: " << term.type << std::endl;
			tState = NEG_NONE;
			break;
		case NEG_SB_NAWS_COL_HIGH:
			if(handleNaws(term.cols, tmpBuf[i], true))
				tState = NEG_SB_NAWS_COL_LOW;
			break;
		case NEG_SB_NAWS_COL_LOW:
			if(handleNaws(term.cols, tmpBuf[i], false))
				tState = NEG_SB_NAWS_ROW_HIGH;
			break;
		case NEG_SB_NAWS_ROW_HIGH:
			if(handleNaws(term.rows, tmpBuf[i], true))
				tState = NEG_SB_NAWS_ROW_LOW;
			break;
		case NEG_SB_NAWS_ROW_LOW:
			if(handleNaws(term.rows, tmpBuf[i], false)) {
				std::cout << "New term size: " << term.cols << " x " << term.rows << std::endl;
				// Some clients (tintin++, cmud, possibly zmud) don't double an IAC(255) when it's
				// sent as data, if this happens in the cols...we should be able to gracefully catch it
				// but if it happens in the rows...it'll eat the IAC from IAC SE, so set the state
				// machine to keep an eye out for a stray SE if the rows were set to 255
				if(term.rows == 255)
					watchBrokenClient = true;
				tState = NEG_NONE;
			}
			break;
		default:
			std::cout << "Unhandled state" << std::endl;
			tState = NEG_NONE;
			break;
		}
	}

	// Handles the screwy windows telnet, and its not that hard for
	// other clients that send \n\r too
	tmp.Replace("\r", "\n");
	inBuf += tmp;

	// handle backspaces
	n = inBuf.getLength();
	for(i = n - tmp.getLength(); i < (unsigned)n; i++) {
		if(inBuf.getAt(i) == '\b') {
			if(n < 2) {
				inBuf = "";
				n = 0;
			} else {
				inBuf.Delete(i-1, 2);
				n -= 2;
				i--;
			}
		}
	}

	bstring::size_type idx = 0;
	while((idx=inBuf.find("\n", 0)) != bstring::npos) {
		bstring tmpr = inBuf.substr(0, idx); // Don't copy the \n
		idx += 1; // Consume the \n
		if(inBuf[idx] == '\n')
			idx += 1; // Consume the extra \n if applicable

		inBuf.erase(0, idx);
		input.push(tmpr);
	}
	ltime = time(0);
	return(0);
}

// Return true if a state should be changed
bool Socket::handleNaws(int& colRow, unsigned char& chr, bool high) {
	// If we get an IAC here, we need a double IAC
	if(chr == IAC) {
		if(!oneIAC) {
//			std::cout << "NAWS: Got ONE IAC, checking for two.\n";
			oneIAC = true;
			return(false);
		} else {
//			std::cout << "NAWS: Got second IAC.\n";
			oneIAC = false;
		}
	} else if(oneIAC && chr != IAC) {
		// Error!
		std::cout << "NAWS: BUG - Expecting a doubled IAC, got " << (unsigned int)chr << "\n";
		oneIAC = false;
	}

	if(high)
		colRow = chr << 8;
	else
		colRow += chr;

	return(true);
}

//********************************************************************
//						processOneCommand
//********************************************************************
// Aka interpreter

int Socket::processOneCommand(void) {
	bstring cmd = input.front();
	input.pop();

	// Send the command to the people we're spying on
	if(!spying.empty()) {
		std::list<Socket*>::iterator it;
		for(it = spying.begin() ; it != spying.end() ; it++ ) {
			Socket *sock = *it;
			if(sock)
				sock->write("[" + cmd + "]\n", false);
		}
	}

	char* tmp = strdup(cmd.c_str());
	((void(*)(Socket*, char*))(fn))(this, tmp);
	free(tmp);

	return(1);
}

//********************************************************************
//						restoreState
//********************************************************************
// Returns a fd to it's previous state

void Socket::restoreState() {
	return(setState(lastState));
}

int restoreState(Socket* sock) {
	sock->restoreState();
	return(0);
}

//*********************************************************************
//						pauseScreen
//*********************************************************************

void pauseScreen(Socket* sock, char *str) {
	if(!strcmp(str, "quit"))
		sock->disconnect();
	else
		sock->reconnect();
}

//*********************************************************************
//						reconnect
//*********************************************************************

void Socket::reconnect(bool pauseScreen) {
	clearSpying();
	clearSpiedOn();

	freePlayer();

	if(pauseScreen) {
		setState(LOGIN_PAUSE_SCREEN);
		printColor("\nPress ^W[RETURN]^x to reconnect or type ^Wquit^x to disconnect.\n: ");
	} else {
		setState(LOGIN_GET_COLOR);
		showLoginScreen();
	}
}

//*********************************************************************
//						setState
//*********************************************************************
// Sets a fd's state and changes the interpreter to the appropriate function

void convertNewWeaponSkills(Socket* sock, char *str);

void Socket::setState(int pState, int pFnParam) {
	// Only store the last state if we're changing states, used mainly in the viewing file states
	if(pState != connState)
		lastState = connState;
	connState = pState;

	if(pState == LOGIN_PAUSE_SCREEN)
		fn = pauseScreen;
	else if(pState > LOGIN_START && pState < LOGIN_END)
		fn = login;
	else if(pState > CREATE_START && pState < CREATE_END)
		fn = createPlayer;
	else if(pState > CON_START && pState < CON_END)
		fn = command;
	else if(pState > CON_STATS_START && pState < CON_STATS_END)
		fn = changingStats;
	else if(pState == CON_CONFIRM_SURNAME)
		fn = doSurname;
	else if(pState == CON_CONFIRM_TITLE)
		fn = doTitle;
	else if(pState == CON_VIEWING_FILE)
		fn = viewFile;
	else if(pState == CON_SENDING_MAIL)
		fn = postedit;
	else if(pState == CON_EDIT_NOTE)
		fn = noteedit;
	else if(pState == CON_EDIT_HISTORY)
		fn = histedit;
	else if(pState == CON_EDIT_PROPERTY)
		fn = Property::descEdit;
	else if(pState == CON_VIEWING_FILE_REVERSE)
		fn = viewFileReverse;
	else if(pState > CON_PASSWORD_START && pState < CON_PASSWORD_END)
		fn = changePassword;
	else if(pState == CON_CHOSING_WEAPONS)
		fn = convertNewWeaponSkills;
	else {
		printf("Unknown connected state!\n");
		fn = login;
	}

	fnparam = pFnParam;
}

int setState(Socket* sock, int state, int pFnParam) {
	sock->setState(state, pFnParam);
	return(0);
}

//********************************************************************
//						bprint
//********************************************************************
// Append a string to the socket's output queue

void Socket::bprint(bstring toPrint) {
	if(!toPrint.empty())
		output += toPrint;
}

//********************************************************************
//						println
//********************************************************************
// Append a string to the socket's output queue with a \n

void Socket::println(bstring toPrint) {
	bprint(toPrint + "\n");
	return;
}

//********************************************************************
//						print
//********************************************************************

void Socket::print(const char* fmt, ...) {
	if(!this)
		return;
	va_list ap;
	va_start(ap, fmt);
	vprint(fmt, ap);
	va_end(ap);
}

//********************************************************************
//						printColor
//********************************************************************

void Socket::printColor(const char* fmt, ...) {
	if(!this)
		return;
	va_list ap;
	va_start(ap, fmt);
	vprint(fmt, ap, true);
	va_end(ap);
}

//********************************************************************
//						flush
//********************************************************************
// Flush pending output and send a prompt

void Socket::flush() {
	if(write(output) == 0)
		return;
	output.clear();

	if(myPlayer && connState != CON_CHOSING_WEAPONS)
		myPlayer->sendPrompt();
}

//********************************************************************
//						write
//********************************************************************
// Write a string of data to the the socket's file descriptor

int Socket::write(bstring toWrite, bool pSpy) {
	int written = 0;
	int n = 0;
	int total = 0;

	toWrite.Replace("\n", "\r\n");
	total = toWrite.length();

	const char *str = toWrite.c_str();
	// Write directly to the socket, otherwise compress it and send it
	if(!opts.compressing) {
		do {
			n = ::write(fd, str + written, total - written);
			if(n < 0)  return(n);
			written += n;
		} while(written < total);
	} else {
		out_compress->next_in = (unsigned char*)str;
		out_compress->avail_in = total;
		while(out_compress->avail_in) {
			out_compress->avail_out = COMPRESSED_OUTBUF_SIZE - ((char*)out_compress->next_out - (char*)out_compress_buf);
			if(deflate(out_compress, Z_SYNC_FLUSH) != Z_OK) {
				return(0);
			}
			written += processCompressed();
			if(written == 0)
				break;
		}
	}

	if(pSpy && !spying.empty()) {
		toWrite.Replace("\n", "\n<Spy> ");
		std::list<Socket*>::iterator it;
		for(it = spying.begin() ; it != spying.end() ; it++ ) {
			Socket *sock = *it;
			if(sock)
				sock->write("<Spy> " + toWrite, false);
		}
	}

	return(written);
}


//--------------------------------------------------------------------
// MCCP

//********************************************************************
//						startCompress
//********************************************************************

int Socket::startCompress() {
	if(!opts.mccp)
		return(-1);

	if(opts.compressing)
		return(-1);

	out_compress_buf = new char[COMPRESSED_OUTBUF_SIZE];
	//out_compress = new z_stream;
	out_compress = (z_stream *)malloc(sizeof(*out_compress));
	out_compress->zalloc = telnet::zlib_alloc;
	out_compress->zfree = telnet::zlib_free;
	out_compress->opaque = NULL;
	out_compress->next_in = NULL;
	out_compress->avail_in = 0;
	out_compress->next_out = (Bytef*)out_compress_buf;
	out_compress->avail_out = COMPRESSED_OUTBUF_SIZE;

	if(deflateInit(out_compress, 9) != Z_OK) {
		// Problem with zlib, try to clean up
		delete out_compress_buf;
		free(out_compress);
		return(-1);
	}

	if(opts.mccp == 2)
		write(telnet::start_mccp2, false);
	else
		write(telnet::start_mccp, false);

	// We're compressing now
	opts.compressing = true;

	return(0);
}

//********************************************************************
//						endCompress
//********************************************************************

int Socket::endCompress() {
	if(out_compress && opts.compressing) {
		unsigned char dummy[1] = {0};
		out_compress->avail_in = 0;
		out_compress->next_in = dummy;
		// process any remaining output first?
		if(deflate(out_compress, Z_FINISH) != Z_STREAM_END)
			return(-1);

		// Send any residual data
		if(processCompressed() < 0)
			return(-1);

		deflateEnd(out_compress);

		delete[] out_compress_buf;

		out_compress = null;
		out_compress_buf = null;

		opts.mccp = 0;
		opts.compressing = false;
		return(0);
	}
	return(-1);
}

//********************************************************************
//						processCompressed
//********************************************************************

int Socket::processCompressed() {
	int len = (int)((char*)out_compress->next_out - (char*)out_compress_buf);
	int written = 0;
	int block;
	int n, i;

	if(len > 0) {
		for(i = 0, n = 0 ; i < len ; i += n) {
			block = tMIN<int>(len - i, 4096);
			if((n = ::write(fd, out_compress_buf + i, block)) < 0)
				return(-1);
			written += n;
			// Keep track of total outbytes
			OutBytes += n;
			if(n == 0)
				break;
		}
		if(i) {
			if(i < len)
				memmove(out_compress_buf, out_compress_buf + i, len - i);

			out_compress->next_out = (Bytef*)out_compress_buf + len - i;
		}
	}
	return(written);
}
// End - MCCP
//--------------------------------------------------------------------

//********************************************************************
//						sendTelnetNeg
//********************************************************************

void Socket::sendTelnetNeg() {
	write(telnet::will_comp2, false);
	write(telnet::will_comp1, false);
	//write(telnet::will_echo);
	write(telnet::do_ttype, false);
	write(telnet::do_naws, false);
	//write(telnet::will_eor, false);
	write(telnet::will_mxp, false);
	write(telnet::will_mssp, false);
}

//********************************************************************
//						ANSI
//********************************************************************

void Socket::ANSI(int color) {
	if(fd > -1 && myPlayer) {
		if(myPlayer->flagIsSet(P_ANSI_COLOR)) {
			if(color & BOLD)			print("%c[%dm", 27, Ansi[COLOR_BOLD]);
			if(color & BLINK)			print("%c[%dm", 27, Ansi[COLOR_BLINK]);
			if(color & NORMAL)			print("%c[%dm", 27, Ansi[COLOR_NORMAL]);

				 if(color & RED)		print("%c[%dm", 27, Ansi[COLOR_RED]);
			else if(color & GREEN)		print("%c[%dm", 27, Ansi[COLOR_GREEN]);
			else if(color & YELLOW)		print("%c[%dm", 27, Ansi[COLOR_YELLOW]);
			else if(color & BLUE)		print("%c[%dm", 27, Ansi[COLOR_BLUE]);
			else if(color & MAGENTA)	print("%c[%dm", 27, Ansi[COLOR_MAGENTA]);
			else if(color & CYAN)		print("%c[%dm", 27, Ansi[COLOR_CYAN]);
			else if(color & BLACK)		print("%c[%dm", 27, Ansi[COLOR_BLACK]);
			else if(color & WHITE)		print("%c[%dm", 27, Ansi[COLOR_WHITE]);

		} else if(myPlayer->flagIsSet(P_MIRC) && color != BLINK) {
			if(color & BOLD)			print("%c", 2);
			if(color & UNDERLINE)		print("%c", 31);
			if(color & NORMAL)			print("%c%c", 3, 3);

				 if(color & RED)		print("%c%d", 3, Mirc[COLOR_RED]);
			else if(color & GREEN)		print("%c%d", 3, Mirc[COLOR_GREEN]);
			else if(color & YELLOW)		print("%c%d", 3, Mirc[COLOR_YELLOW]);
			else if(color & BLUE)		print("%c%d", 3, Mirc[COLOR_BLUE]);
			else if(color & MAGENTA)	print("%c%d", 3, Mirc[COLOR_MAGENTA]);
			else if(color & CYAN)		print("%c%d", 3, Mirc[COLOR_CYAN]);
			else if(color & BLACK)		print("%c%d", 3, Mirc[COLOR_BLACK]);
			else if(color & WHITE)		print("%c%d", 3, Mirc[COLOR_WHITE]);

		}
	}
}

//********************************************************************
//						hasOutput
//********************************************************************

bool Socket::hasOutput(void) const {
	return(!output.empty());
}

//********************************************************************
//						hasCommand
//********************************************************************

bool Socket::hasCommand(void) const {
	return(!input.empty());
}

//********************************************************************
//						canForce
//********************************************************************
// True if the socket is playing (ie: fn is command and fnparam is 1)

bool Socket::canForce(void) const {
	return(fn == (void(*)(Socket*, char *))::command && fnparam == 1);
}

//********************************************************************
//						get
//********************************************************************

int Socket::getState(void) const {
	if(!this)
		return(UNUSED_STATE);
	return(connState);
}
bool Socket::isConnected() const {
	return(connState < LOGIN_START && connState != CON_DISCONNECTING);
}
bool Player::isConnected() const {
	return(getSock()->isConnected());
}

int Socket::getFd(void) const { return(fd); }
bool Socket::getMxp(void) const { return(opts.mxp); }
int Socket::getMccp(void) const { return(opts.mccp); }
bool Socket::getEor(void) const { return(opts.eor); }
long Socket::getIdle(void) const { return(time(0) - ltime); }
const bstring& Socket::getIp(void) const { return(host.ip); }
const bstring& Socket::getHostname(void) const { return(host.hostName); }
bstring Socket::getTermType() const { return(term.type); }
int Socket::getTermCols() const { return(term.cols); }
int Socket::getTermRows() const { return(term.rows); }

int Socket::getParam() { return(fnparam); }
void Socket::setParam(int newParam) { fnparam = newParam; }

void Socket::setHostname(bstring pName) { host.hostName = pName; }
void Socket::setIp(bstring pIp) { host.ip = pIp; }
void Socket::setPlayer(Player* ply) { myPlayer = ply; }

Player* Socket::getPlayer() const {
	if(!this)
		return(NULL);
	return(myPlayer);
}


// MCCP Hooks
void *zlib_alloc(void *opaque, unsigned int items, unsigned int size) {
	return calloc(items, size);
}
void zlib_free(void *opaque, void *address) {
	free(address);
}

//********************************************************************
//						nonBlock
//********************************************************************

int nonBlock(int pFd) {
	int flags;
	flags = fcntl(pFd, F_GETFL, 0);
	flags |= O_NONBLOCK;
	if(fcntl(pFd, F_SETFL, flags) < 0)
		return(-1);
	return(0);
}


//*********************************************************************
//						showLoginScreen
//*********************************************************************

void Socket::showLoginScreen(bool dnsDone) {
	//*********************************************************************
	// As a part of the copyright agreement this section must be left intact
	// Feel free to add to it, but leave the existing stuff here.
	//*********************************************************************
	print("Your Mudname Here (RoH beta v" VERSION ")\n\tBased on Mordor by Brett Vickers, Brooke Paul.\n");
	print("Programmed by: Jason Mitchell, Randi Mitchell and Tim Callahan.\n");
	print("Contributions by: Jonathan Hseu.");

	char file[80];
	sprintf(file, "%s/login_screen.txt", CONFPATH);
	viewLoginFile(this, file);

	if(dnsDone)
		checkLockOut();
	flush();
}


//********************************************************************
//				         askFor
//********************************************************************

void Socket::askFor(const char *str) {
	ASSERTLOG( str );
	if(getEor() == 1) {
		char eor_str[] = {IAC, EOR, '\0' };
		printColor(str );
		print(eor_str);
	} else {
		printColor(str );
	}
}


unsigned const char mssp_val[] = {MSSP_VAL, '\0'};
unsigned const char mssp_var[] = {MSSP_VAR, '\0'};

void addMSSPVar(std::ostringstream& msspStr, bstring var) {
	msspStr << mssp_var << var;
}

template<class T>
void addMSSPVal(std::ostringstream& msspStr, T val) {
	msspStr << mssp_val << val;
}

int Socket::sendMSSP() {
	std::cout << "Sending MSSP string\n";

	std::ostringstream msspStr;

	msspStr << telnet::sb_mssp_start;
	addMSSPVar(msspStr, "NAME");
	addMSSPVal<bstring>(msspStr, "UnNamed");

	addMSSPVar(msspStr, "PLAYERS");
	addMSSPVal<int>(msspStr, gServer->getNumPlayers());

	addMSSPVar(msspStr, "UPTIME");
	addMSSPVal<long>(msspStr, StartTime);

	addMSSPVar(msspStr, "HOSTNAME");
	addMSSPVal<bstring>(msspStr, "<YourHostname>");

	addMSSPVar(msspStr, "PORT");
	addMSSPVal<bstring>(msspStr, "23");
	addMSSPVal<bstring>(msspStr, "3333");

	addMSSPVar(msspStr, "CODEBASE");
	addMSSPVal<bstring>(msspStr, "RoH beta v" VERSION);

	addMSSPVar(msspStr, "CREATED");
	addMSSPVal<bstring>(msspStr, "1998");

	addMSSPVar(msspStr, "LANGUAGE");
	addMSSPVal<bstring>(msspStr, "English");

	addMSSPVar(msspStr, "LOCATION");
	addMSSPVal<bstring>(msspStr, "United States");

	addMSSPVar(msspStr, "WEBSITE");
	addMSSPVal<bstring>(msspStr, "http://www.rohonline.net");

	addMSSPVar(msspStr, "FAMILY");
	addMSSPVal<bstring>(msspStr, "Mordor");

	addMSSPVar(msspStr, "GENRE");
	addMSSPVal<bstring>(msspStr, "Fantasy");

	addMSSPVar(msspStr, "GAMEPLAY");
	addMSSPVal<bstring>(msspStr, "Roleplaying");
	addMSSPVal<bstring>(msspStr, "Hack and Slash");
	addMSSPVal<bstring>(msspStr, "Adventure");

	addMSSPVar(msspStr, "STATUS");
	addMSSPVal<bstring>(msspStr, "Live");

	addMSSPVar(msspStr, "GAMESYSTEM");
	addMSSPVal<bstring>(msspStr, "Custom");

	addMSSPVar(msspStr, "AREAS");
	addMSSPVal<int>(msspStr, -1);

	addMSSPVar(msspStr, "HELPFILES");
	addMSSPVal<int>(msspStr, -1);

	addMSSPVar(msspStr, "MOBILES");
	addMSSPVal<int>(msspStr, -1);

	addMSSPVar(msspStr, "OBJECTS");
	addMSSPVal<int>(msspStr, -1);

	addMSSPVar(msspStr, "ROOMS");
	addMSSPVal<int>(msspStr, -1);

	addMSSPVar(msspStr, "CLASSES");
	addMSSPVal<int>(msspStr, gConfig->classes.size());

	addMSSPVar(msspStr, "LEVELS");
	addMSSPVal<int>(msspStr, MAXALVL);

	addMSSPVar(msspStr, "RACES");
	addMSSPVal<int>(msspStr, gConfig->getPlayableRaceCount());

	addMSSPVar(msspStr, "SKILLS");
	addMSSPVal<int>(msspStr, gConfig->skills.size());

	addMSSPVar(msspStr, "ANSI");
	addMSSPVal<bstring>(msspStr, "1");

	addMSSPVar(msspStr, "MCCP");
	addMSSPVal<bstring>(msspStr, "1");

	addMSSPVar(msspStr, "MXP");
	addMSSPVal<bstring>(msspStr, "1");

	addMSSPVar(msspStr, "PAY TO PLAY");
	addMSSPVal<bstring>(msspStr, "0");

	addMSSPVar(msspStr, "PAY FOR PERKS");
	addMSSPVal<bstring>(msspStr, "0");

	addMSSPVar(msspStr, "HIRING BUILDERS");
	addMSSPVal<bstring>(msspStr, "1");

	addMSSPVar(msspStr, "HIRING CODERS");
	addMSSPVal<bstring>(msspStr, "1");

	addMSSPVar(msspStr, "MULTICLASSING");
	addMSSPVal<bstring>(msspStr, "1");

	addMSSPVar(msspStr, "NEWBIE FRIENDLY");
	addMSSPVal<bstring>(msspStr, "1");

	addMSSPVar(msspStr, "PLAYER CLANS");
	addMSSPVal<bstring>(msspStr, "0");

	addMSSPVar(msspStr, "PLAYER CRAFTING");
	addMSSPVal<bstring>(msspStr, "1");

	addMSSPVar(msspStr, "PLAYER GUILDS");
	addMSSPVal<bstring>(msspStr, "1");

	addMSSPVar(msspStr, "EQUIPMENT SYSTEM");
	addMSSPVal<bstring>(msspStr, "Both");

	addMSSPVar(msspStr, "MULTIPLAYING");
	addMSSPVal<bstring>(msspStr, "Restricted");

	addMSSPVar(msspStr, "PLAYERKILLING");
	addMSSPVal<bstring>(msspStr, "Restricted");

	addMSSPVar(msspStr, "QUEST SYSTEM");
	addMSSPVal<bstring>(msspStr, "Integrated");

	addMSSPVar(msspStr, "ROLEPLAYING");
	addMSSPVal<bstring>(msspStr, "Encouraged");

	addMSSPVar(msspStr, "TRAINING SYSTEM");
	addMSSPVal<bstring>(msspStr, "Both");

	addMSSPVar(msspStr, "WORLD ORIGINALITY");
	addMSSPVal<bstring>(msspStr, "All Original");

	msspStr << telnet::sb_mssp_end;

	return(write(msspStr.str()));
}

//unsigned const char sb_mssp_start[] = {IAC, SB, TELOPT_MSSP, '\0'};
//// MSSP SB stop
//unsigned const char sb_mssp_end[] = {IAC, SE, '\0'};