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