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