musicmud-2.1.6/data/
musicmud-2.1.6/data/help/
musicmud-2.1.6/data/policy/
musicmud-2.1.6/data/wild/
musicmud-2.1.6/data/world/
musicmud-2.1.6/doc/
musicmud-2.1.6/src/ident/
musicmud-2.1.6/src/lua/
musicmud-2.1.6/src/lua/include/
musicmud-2.1.6/src/lua/src/lib/
musicmud-2.1.6/src/lua/src/lua/
musicmud-2.1.6/src/lua/src/luac/
/* 
 * MusicMUD Daemon, version 1.0
 * Copyright (C) 1997-2003 Abigail Brady
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 */

#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>

#include "msi.h"
#include "musicmud.h"
#include "Socket.h"
#include "config.h"
#include "pflags.h"
#include "vsprintf.h"
#include "charset.h"
#include "misc.h"
#include "State.h"
#include "pipe-ident.h"

#define TELOPTS
#define TELCMDS

//#define LINEEDITOR
// define this to use the line editor

#include <arpa/telnet.h>

Socket *primary;
Socket *secondary;
Socket *websock;

namespace msi {
  int i_port = 0;
};

void msi::set_port(int p) {
  i_port = p;
}

int msi::port() {
  return i_port;
}

void msi::init() {
  msi::init(i_port);
}

void msi::init(int port) {
  primary = new Socket(port);
  if (port == MUD_DEFPORT)
	  secondary = new Socket(port+1);
  else
          secondary = new Socket(port+3);

  websock = new Socket(port+10);
 
  i_port = port;
}

void msi::done() {
	delete primary;
	primary = 0;
	delete secondary;
	secondary = 0;
	delete websock;
	websock = 0;
}


/*
 *  FF 255 377  IAC
 *  FE     376  DONT
 *  FD     375  DO
 *  FC     374  WONT
 *  FB     373  WILL
 *  FA     372  SB
 * 
 *
 */

#ifndef NOCOMPRESS
#define TELOPT_COMPRESS 85
#define TELOPT_COMPRESS2 86
#endif

#define TELOPT_MXP 0x5b

#define TELOPT_MPLEX  0x70
#define MPLEX_SELECT  0x71
#define MPLEX_HIDE    0x72
#define MPLEX_SHOW    0x73
#define MPLEX_SETSIZE 0x74

void msi::player::ask_things() {
  char mxp[3] = { IAC, WILL, TELOPT_MXP };
  rawsend(mxp, 3);
#ifndef NOCOMPRESS
  char comp2[3] = { IAC, WILL, TELOPT_COMPRESS2 };
  rawsend(comp2, 3);

  char comp1[3] = { IAC, WILL, TELOPT_COMPRESS };
  rawsend(comp1, 3);
#endif
  char eor[3] = { IAC, WILL, TELOPT_EOR };
  rawsend(eor, 3);

  char type[3] = { IAC, DO, TELOPT_TTYPE };
  rawsend(type, 3);

  char naws[3] = { IAC, DO, TELOPT_NAWS };
  rawsend(naws, 3);

  char mplex[3] = { IAC, WILL, TELOPT_MPLEX };
  rawsend(mplex, 3);

  char env[3] = { IAC, DO, TELOPT_NEW_ENVIRON };
  rawsend(env, 3);
#ifdef LINEEDITOR
  char sga[3] = { IAC, WILL, TELOPT_SGA };
  rawsend(sga, 3);

  char ech[3] = { IAC, WILL, TELOPT_ECHO };
  rawsend(ech, 3);
#endif
}

void msi::player::mplex_choose(int i) {
  if (!mplex)
    return;

  char blah[] = { IAC, SB, TELOPT_MPLEX, MPLEX_SELECT,i, IAC, SE };
  rawsend(blah, sizeof blah);
  if (i) {
    char home[] = {'\033', '[', 'H' };
    rawsend(home, sizeof home);
  }
}

void msi::player::mplex_show(int i) {
  if (!mplex)
    return;

  char blah[] = { IAC, SB, TELOPT_MPLEX, MPLEX_SHOW, i, IAC, SE };
  rawsend(blah, sizeof blah);
}

string iac(unsigned char c)
{
  if (c == 0xff)
    return "\xff\xff";
  string s;
  s += c;
  return s;

}

void msi::player::mplex_setsize(int i, int cols, int rows) {
  if (!mplex)
    return;

  string blah;
  blah += IAC;
  blah += SB;
  blah += TELOPT_MPLEX;
  blah += MPLEX_SETSIZE;
  blah += i;

  blah += iac(((cols & 0xff00)>>8));
  blah += iac(((cols & 0x00ff)>>0));

  blah += iac(((rows & 0xff00)>>8));
  blah += iac(((rows & 0x00ff)>>0));

  blah += IAC;
  blah += SE;

  rawsend(blah);
}

void msi::player::mplex_hide(int i) {
  if (!mplex)
    return;

  char blah[] = { IAC, SB, TELOPT_MPLEX, MPLEX_HIDE, i, IAC, SE };
  rawsend(blah, sizeof blah);
}

struct {
  Socket **s;
  const char *log;
  msi::Thingy thingy;
  int dotelnet;
  int lev;
} ports[] = {
  
  { &primary, "new connection from %s is %s", msi::tNormal, 1, 0 },
  { &secondary, "mono connection from %s is %s", msi::tMono, 1, 0 },
  { &websock, "webmake connection from %s", msi::tWebmake, 0, LEV_INTERNAL },

};

#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))

msi::player *msi::player::accept(fd_set *fdset) {
  for (size_t i=0;i<ARRAY_SIZE(ports);i++) {
    if (!(*ports[i].s) || (*(ports[i].s))->get_dead())
      continue;

    if (*ports[i].s && FD_ISSET((*ports[i].s)->getfd(), fdset)) {
      Socket *s = (*ports[i].s)->accept();
      if (!s)
	continue;

      msi::player *p = new msi::player;
      
      p->socket = s;
      p->thingy_from = ports[i].thingy;
      
      extern int which_player;
      string pid = ssprintf("@player_%i", which_player);
 
      log(PFL_HOSTS, ports[i].lev, "net",
	  ports[i].log, 
	  p->get_hostname().c_str(), 
	  pid.c_str());
      
      if (ports[i].dotelnet) {
	p->ask_things();
	p->flush();
      }

#ifdef LINEEDITOR
      p->lineeditor = 1;
#endif

      return p;
    }
  }
  return 0;
}

void msi::player::keepalive() {
  if (rawmode)
    return;

  char keepalive[2] = { IAC, NOP };
  rawsend(keepalive, 2);
  flush();
  last_keepalive = now;
}


int msi::player::get_port() const {
  return socket->get_addr()->get_port();
}

string msi::player::get_hostname() const {
  string s = ident::dnslookup(socket->get_addr());
  if (s.length())
    return s;
  return get_ip();
}

string msi::player::get_ip() const {
    return socket->get_addr()->tostring();
}


string default_colour_parse(colstate_t *, const char *src, const colourinfo_t) {
  string t;
  int dont = 0;
  while (*src) {
    if (*src=='\3') 
      /* 3 marks the start of an mxp tag. we ignore any stuff in one. */
      dont++;
    if (*src=='\4') 
      /* 4 marks the end of an mxp tag */
      dont--;
    else if (*src=='\n' || *src=='\v')
      /* this bit's required by the telnet protocol */
      t += "\r\n";
    else if (dont<1 && *src!='\2')
      /* 2 is used to indicate the end of an escape sequence, for wordwrap purposes,
	 etc. also to provide a seperator between user supplied text and regular text,
	 to stop user-supplied partial colour codes from latching onto the mud-provided one. */
      t += *src;
    src++;
  }
  return t;
}

string iaciac(string a)
{
  const unsigned char *b = (const unsigned char*)a.c_str();
  string tmp;
  while (*b) {
    if (*b==0xff)
      tmp += *b;
    tmp += *b;
    b++;
  }
  return tmp;
}

void msi::player::write(const char *what, const colourinfo_t &ci) {
  if (!what || !*what)
    return;

  if (rawmode) {
    rawsend(what);
    return;
  }

  string s = (msi_colourfilter?msi_colourfilter:default_colour_parse)(&colstate, what, ci);
  s = iaciac(s);
  rawsend(s);
}

void msi::player::rawsend(const string &txt) {
  rawsend(txt.data(), txt.length());
}

void msi::player::rawsend(const char *data, size_t len)
{
  if (!len)
    return;

  last_keepalive = now;

  string tosend = "";
#ifndef NOCOMPRESS
  int neededs = 0;
  if (compress && deflate) {
    deflate->next_out = small_outbuf;
    deflate->avail_out = SMALL_BUFSIZE;    

    deflate->avail_in = len;
    deflate->next_in = (Bytef*)data;

    int result = 1;

    uncbytes += len;

    do {
      int df, prevsize = SMALL_BUFSIZE - deflate->avail_out;

      /* If there is input or the output has reset from being previously full, 
	 run compression again. */
      if (deflate->avail_in || deflate->avail_out == SMALL_BUFSIZE)
        if ((df = ::deflate(deflate, Z_SYNC_FLUSH)) != 0) {
          log(PFL_SEEINFO, LEV_ADMIRAL, "compress", "deflate() returned %d.", df);
	}

      string t((const char *)small_outbuf + prevsize,
	       (SMALL_BUFSIZE - deflate->avail_out - prevsize));
      tosend += t;
      neededs++;
      
      combytes += SMALL_BUFSIZE - deflate->avail_out - prevsize;

      /* Wrap the buffer when we've run out of buffer space for the output. */
      if (deflate->avail_out == 0) {
        deflate->avail_out = SMALL_BUFSIZE;
        deflate->next_out = small_outbuf;
      }

      if (result <= 0) {
	log(PFL_SEEINFO, LEV_ADMIRAL, "compress", "there were bytes left over!");
        return;
      }

    /* Need to loop while we still have input or when the output buffer was previously full. */
    } while (deflate->avail_out == SMALL_BUFSIZE || deflate->avail_in);

    data = tosend.data();
    len = tosend.length();
  }

  rawbytes += len;
#endif
  outbuffer += string(data, len);
}

void msi::player::flush() {
  if (outbuffer.length() && !nosend) {
    int sent = socket->write(outbuffer.data(), outbuffer.length());
    if (sent == -1 || sent != outbuffer.length()) {
      nosend = 1;
    }
    outbuffer = outbuffer.substr(sent);
  }
}

void msi::player::eor() {
  if (do_eor && !lineeditor && term!="zmud") {
    char eor[] = { IAC, EOR };
    rawsend(eor, sizeof eor);
  }
}

string make_lower(const string &s)
{
  return make_lower(s.c_str());
}

void msi::player::datah(unsigned char data, commandhandler *pl)
{
  /* we could do a simple line editor here sometime, for people
     stuck using windows telnet. for now, this will have to do. */
  int curcol = 0; // XXX prompt length

  if (data == '\b' || data == 0x7f) {
    if (cursor)
      {
	buffer.erase(buffer.length()-1);
	cursor--;

	if (lineeditor && !last_noecho) {	
	  if ((curcol+cursor%80)==79) {
	    char d[] = "\r\033[A\033[79C ";
	    if (data)
	      rawsend(d, strlen(d));
	  } else {
	    char d[3] = {'\b', ' ', '\b' };
	    if (data)
	      rawsend(d, 3);
	  }
	}	
      }
    return;
  }
  /* newline algorithm - the first of \n or \r we see, we will recognise
     as the newline character.

     the other one will always be ignored unless there is data pending.
     this means that the only bad thing to happen if the newlines mess
     around at runtime, is that blank lines may be ignored.
  */
  if (!newlinechar) {
    if (data == '\n') newlinechar = '\n';
    if (data == '\r') newlinechar = '\r';
  }

#if 0
  if (isprint(data))
    printf("%c\n", data);
  else
    printf("%2x\n", data);
#endif

  if (data == '\r' || data == '\n') {
    if (secure) {
      if (make_lower(buffer.substr(0, strlen("<VERSION ")))==
	  "<version ") {
	string s = make_lower(buffer.substr(strlen("<VERSION ")));
	if (s.find("mxp=")!=string::npos) {

	  string mxpversion = s.substr(s.find("mxp=")+4);

	  if (mxpversion[0]=='"') {
	    mxpversion = mxpversion.substr(1);
	    mxpversion = mxpversion.substr(0, mxpversion.find('"'));
	  } else if (mxpversion[0]=='\'') {
	    mxpversion = mxpversion.substr(1);
	    mxpversion = mxpversion.substr(1, mxpversion.find('\''));
	  } else {
	    mxpversion = mxpversion.substr(0, mxpversion.find(' '));
	  }
	  
	  mxp_version = mxpversion;
	  if (mxp_version >= "0.5") {	  
	    rawsend("\e[6z");
	    secure_locked = 1;
	  }
	}
      }

      buffer = "";
      cursor = 0;
      secure = 0;
      return;
    }

    if (trunc) {
      trunc = 0;
      rawsend("Warning: command truncated.\r\n");
    }

    if (cursor || data == newlinechar) {
      buffer[cursor] = 0;

      if (lineeditor) {	
	if (!last_noecho)
	  rawsend("\r\n", 2);
      }

      pl->input(buffer.c_str());

      buffer = "";
      cursor = 0;
    }
    return;
  }
  if (data < 0x20 && data != 033)
    return;

  if (data == 033) {
    esc = 1;
    escstr = "";
    return;
  }

  if (esc) {
    escstr += data;
  }

  if (escstr=="[1z") {
    secure = 1;
    escstr = "";
    esc = 0;
    return;
  }

  if (esc && (isalpha(data)||data=='~')) {
    esc = 0;
    return;
  }

  if (esc) {
    return;
  }

  if (lineeditor && !last_noecho) {
    if ((curcol+cursor%80)==79) {
      char d[] = {'\n', data};
      rawsend(d, strlen(d));
    } else {
      char d[2] = { data };
      if (data)
	rawsend(d, 1);
    }
  }

  if (cursor > 1000) {
    trunc = 1;
    return;
  }

  buffer += data;
  cursor++;
}

#define SB_IAC  0x100
#define SB_DATA 0x101

void msi::player::handle_naws(commandhandler *pl, const string &data) {
  if (data.length()!=4) {
    /* improper length : ignore */
    return;
  }
  const unsigned char *d = (const unsigned char *)data.c_str();
  col = (d[0] << 8) | d[1];
  row = (d[2] << 8) | d[3];
  pl->size_changed();
}

void msi::player::handle_ttype(commandhandler *pl, const string &data)
{
  if (data.length() < 2) {
    return;
  }

  term = "";

  const char *d = data.c_str();

  if (data[0] == TELQUAL_IS) {
    /* this is the standard format, but some clients don't bother with it. accept both */
    d++;
  }

  while (*d) {
    if (isprint(*d))
      term += tolower(*d);
    else
      term += "?";
    d++;
  }
}

#ifdef TELNET_DEBUG
string iac(unsigned char d) {
  if (d > xEOF)
    return telcmds[d - xEOF];
  if (d < NTELOPTS)
    return telopts[d];
  if (d == TELOPT_MXP)
    return "TELOPT_MXP";
  char b[10];
  sprintf(b, "%2x (%c)", d, d);
  return b;
}
#endif

void ser(string &c, unsigned int i)
{
  string cmd;
  cmd += ((i>>24) & 0xff);
  cmd += ((i>>16) & 0xff);
  cmd += ((i>> 8) & 0xff);
  cmd += ((i>> 0) & 0xff);
  
  c += cmd;
#ifdef DEBUG_SER
  printf("set %5i as %02x%02x%02x%02x\n", i, (unsigned char)cmd[0], 
	 (unsigned char)cmd[1], (unsigned char)cmd[2], (unsigned char)cmd[3]);
#endif
}

static void ser(string &s, string const &str)
{
#ifdef DEBUG_SET
  printf("writing string '%s' (length %i).\n", str.c_str(), str.length());
#endif
  ser(s, str.length());
  for (size_t i=0;i<str.length();i++) {
    s += str[i];
  }
}

static void deser(string &c, int &i)
{
  if (c.length()<4) {
#ifdef DEBUG_SER
    printf("Underflow!\n");
#endif
    return;
  }
  const unsigned char *cmd = (const unsigned char *)c.c_str();
#ifdef DEBUG_SER
  printf("read as %02x%02x%02x%02x\n", (unsigned char)cmd[0], 
	 (unsigned char)cmd[1], (unsigned char)cmd[2], (unsigned char)cmd[3]);
#endif
  i = 0;
  for (int j=0;j<4;j++) {
    i <<= 8;
    i |= cmd[j];
  }
  c = c.substr(4);
}

#ifndef TIMET_IS_INT
static void deser(string &cmd, time_t &t) {
  int i;
  deser(cmd, i);
  t = i;
}
#endif

static void deser(string &cmd, size_t &t) {
  int i;
  deser(cmd, i);
  t = i;
}

void deser(string &s, string &str)
{
  size_t len;
  deser(s, len);
  if (s.length()<len) {
#ifdef DEBUG_SER
    printf("Underflow!\n");
#endif
    return;
  }
  str = "";
  for (size_t i=0;i<len;i++) {
    str += s[i];
  }
  s = s.substr(len);
}

int x(char c)
{
  if (isdigit(c)) return c-'0';
  c = tolower(c);
  if (c == 'a') return 10;
  if (c == 'b') return 11;
  if (c == 'c') return 12;
  if (c == 'd') return 13;
  if (c == 'e') return 14;
  if (c == 'f') return 15;
  return 0;
}

string hex(string str) {
  string s2;
  for (size_t i=0;i<str.length();i++) {
    char b[3];
    sprintf(b, "%02X", (unsigned char)str[i]);
    s2 += b;
  }
  return s2;
}

string unhex(string s) {
  const char *s2 = s.c_str();
  string str = "";
  while (*s2) {
    unsigned char ch = (x(s2[0]) << 4) | (x(s2[1]));
    str += ch;
    s2 += 2;
  }
  return str;
}

string serialise_personstate(PersonState *ps) {
  string s;

  ser(s, ps->strs.size());
  base::strit si = ps->strs.begin();
  while (si != ps->strs.end()) {
    ser(s, si->first);
    ser(s, si->second);
    si++;
  }

  ser(s, ps->ints.size());
  base::intit ii = ps->ints.begin();
  while (ii != ps->ints.end()) {
    ser(s, ii->first);
    ser(s, ii->second);
    ii++;
  }

  return hex(s);
}

void deserialise_personstate(PersonState *ps, string s)
{
  s = unhex(s);

  size_t strcount;
  deser(s, strcount);
  for (size_t i=0;i<strcount;i++) {
    string key;
    string val;

    deser(s, key);
    deser(s, val);
    
    ps->set(key.c_str(), val);
  }

  size_t intcount;
  deser(s, intcount);
  for (size_t i=0;i<intcount;i++) {
    string key;
    size_t val;

    deser(s, key);
    deser(s, val);

    ps->set(key.c_str(), val);
  }
}

string msi::player::serialise() const {
  string str;
  ser(str, do_eor);
  ser(str, mxp);
  ser(str, row);
  ser(str, col);
  ser(str, rawmode);
  ser(str, last_keepalive); /* XXX asume time_t is int */
  ser(str, term);
  ser(str, last_noecho);
  ser(str, se_mode);
  ser(str, mode);
  ser(str, se_str);
  ser(str, colstate.fgcol);
  ser(str, colstate.bgcol);
  ser(str, colstate.bold);
  ser(str, colstate.ital);
  ser(str, colstate.undl);
  ser(str, colstate.dec);
#ifndef NOCOMPRESS
  ser(str, compress);
#else
  ser(str, 0);
#endif
  ser(str, thingy_from);
#ifndef NOCOMPRESS
  ser(str, will_compress);
#else
  ser(str, 0);
#endif
  ser(str, mplex);
  ser(str, lineeditor);
  ser(str, esc);
  ser(str, escstr);
  ser(str, secure);
  ser(str, secure_locked);
  ser(str, colstate.sout);
  ser(str, mxp_version);
  string s2;
  for (size_t i=0;i<str.length();i++) {
    char b[3];
    sprintf(b, "%02X", (unsigned char)str[i]);
    s2 += b;
  }
  return s2;
}

void msi::player::deserialise(const string &s) {
#ifdef NOCOMPRESS
  int junk;
#endif
  const char *s2 = s.c_str();
  string str = "";
  while (*s2) {
    unsigned char ch = (x(s2[0]) << 4) | (x(s2[1]));
    str += ch;
    s2 += 2;
  }
  deser(str, do_eor);
  deser(str, mxp);
  deser(str, row);
  deser(str, col);
  deser(str, rawmode);
  deser(str, last_keepalive); /* XXX asume time_t is int */
  deser(str, term);
  deser(str, last_noecho);
  deser(str, se_mode);
  deser(str, mode);
  deser(str, se_str);
  deser(str, colstate.fgcol);
  deser(str, colstate.bgcol);
  deser(str, colstate.bold);
  deser(str, colstate.ital);
  deser(str, colstate.undl);
  deser(str, colstate.dec);
#ifndef NOCOMPRESS
  deser(str, compress);
#else
  deser(str, junk);
#endif
  int thingy;
  deser(str, thingy);
  thingy_from = Thingy(thingy);
#ifndef NOCOMPRESS
  deser(str, will_compress);
#else
  deser(str, junk);
#endif
  deser(str, mplex);
  deser(str, lineeditor);
  deser(str, esc);
  deser(str, escstr);
  deser(str, secure);
  deser(str, secure_locked);
  deser(str, colstate.sout);
  deser(str, mxp_version);
  if (str.length()) {
    printf("XXX : strlen is %i.\n", str.length());
  }
#ifndef NOCOMPRESS
  if (compress) {
    compress = 0;
    compress_on();
  }
#endif
}

#ifndef NOCOMPRESS
static void *zlib_alloc(void *opaque, unsigned int items, unsigned int size)
{
    return calloc(items, size);
}

static void zlib_free(void *opaque, void *address)
{
    free(address);
}
#endif

#ifdef TELNET_DEBUG
string iacs(int a)
{
  if (a==0) return "";
  if (a==256) return "SB";
  if (a==257) return "SBI";
  return iac(a);
}
#endif
void msi::player::tstack(unsigned char data, commandhandler *pl) {
#ifdef TELNET_DEBUG
  printf("state: %10s ", iacs(mode).c_str());
#endif
  if (mode==0) {
    if (data==IAC) {
#ifdef TELNET_DEBUG
      printf("IAC\n");
#endif
      mode = IAC;
      return;
    }
#ifdef TELNET_DEBUG
    if (isprint(data)) {
      printf("%2x (%c)\n", data, data);
    } else {
      printf("%2x\n", data);
    }
#endif  
    datah(data, pl);
    return;
  }

  if (mode==SB_DATA) {
    if (data==IAC) {
      mode = SB_IAC;
#ifdef TELNET_DEBUG
  printf("%s\n", iac(data).c_str());
#endif
      return;
    }
    if (se_str.length()<40) {
      se_str += data;
    }
#ifdef TELNET_DEBUG
    if (isprint(data)) {
      printf("  %2x (%c)\n", data, data);
    } else {
      printf("  %2x\n", data);
    }
#endif
    return;
  }  
#ifdef TELNET_DEBUG
  printf("%s\n", iac(data).c_str());
#endif
  if (mode==IAC) {
    if (data==IAC) {
      mode = 0;
      datah(data, pl);
      return;
    }
    if (data==NOP || data == IP || data == GA) {
      mode = 0;
      return;
    }
    if (data==WILL || data == DO || data == WONT || data==DONT) {
      mode = data;
      return;
    }
    if (data==SB) {
      mode = SB;
      return;
    }
    mode = 0;
    return;
  }

  if (mode==SB) {
    se_str = "";
    se_mode = data;
    mode = SB_DATA;
    return;
   
  }

  if (mode==SB_IAC) {
    if (data==SE) {
      if (se_mode == TELOPT_NAWS) {
	handle_naws(pl, se_str);
      } else if (se_mode == TELOPT_TTYPE) {
	handle_ttype(pl, se_str);
      } else if (se_mode == TELOPT_NEW_ENVIRON) {
	if (se_str[0]==TELQUAL_IS &&
	    se_str[1]==NEW_ENV_VAR &&
	    se_str[2]=='U' &&
	    se_str[3]=='S' &&
	    se_str[4]=='E' &&
	    se_str[5]=='R' &&
	    se_str[6]==NEW_ENV_VALUE) {
#if 0
	  if (pl && pl->get_state() && streq(pl->get_state()->id, "name")) {
	    pl->input(se_str.c_str()+7);
	  }
#endif
	  pl->username(se_str.c_str()+7);
	}
      }
      mode = 0;
      return;
    }
    if (data==IAC) {
      se_str += data;
      return;
    }
    /* I am assuming nothing is going to happen in a subnegotiation apart from 
       IAC SE and IAC IAC. I really hope this is the case. */
    mode = 0;
    return;
  }

  if (mode==WILL) {    
    if (data == TELOPT_NEW_ENVIRON) {
      const char blah[] = { IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, NEW_ENV_VAR, 
			    'U', 'S', 'E', 'R', IAC, SE };
      rawsend(blah, sizeof blah);      
      mode = 0;
      return;
    }

    if (data == TELOPT_TTYPE) {	      
      const char blah[] = { IAC, SB, TELOPT_TTYPE, 1, IAC, SE };
      rawsend(blah, 6);
      mode = 0;
      return;
    }

    if (data == TELOPT_NAWS) {
      mode = 0;
      return;
    }
#if 0
    char refuse[3] = {IAC, DONT, data};
    rawsend(refuse, 3);
#endif
    mode = 0;
    return;
  }

  if (mode==DO) {
    if (data == TELOPT_MXP) {
      char start[6] = {IAC, SB, TELOPT_MXP, IAC, SE};
      rawsend(start, 5);
      
      const char *initstr = 
	"\033[1z<!ELEMENT Ex '<send>' Flag=RoomExit>"
	"<!ELEMENT RDesc Flag=RoomDesc>"
	"<!ELEMENT RName Flag=RoomName><VERSION>";
      
      rawsend(initstr, strlen(initstr));
      mxp = 1;
      mode = 0;
      return;
    }
    if (data == TELOPT_MPLEX) {
      mplex = 1;
      mode = 0;
      return;
    }
#ifndef NOCOMPRESS
    if (data == TELOPT_COMPRESS2) {
      if (!will_compress) {
	will_compress = 2;
	compress_on();
      }
      mode = 0;
      return;
    }
    if (data == TELOPT_COMPRESS) {
      if (!will_compress) {
	will_compress = 1;
	compress_on();
      }
      mode = 0;
      return;
    }
#endif
    if (data == TELOPT_TM) {
      char refuse[3] = {IAC, WONT, TELOPT_TM};
      rawsend(refuse, 3);
      mode = 0;
      return;
    }

    if (data == TELOPT_EOR) {
      do_eor = 1;
      char now[2] = { IAC, EOR };
      rawsend(now, 2);
      mode = 0;
      return;
    }

    if (data == TELOPT_ECHO) {
      mode = 0;
      return;
    }
#if 0
    char refuse[3] = {IAC, WONT, data};
    rawsend(refuse, 3);
#endif
    mode = 0;
    return;
  }

  if (mode==WONT) {
    if (data == TELOPT_NAWS) {
      mode = 0;
      return;
    }

    if (data == TELOPT_TTYPE) {
      mode = 0;
      return;
    }

    /* IAC DONT foo */
    mode = 0;
    return;
  }

  if (mode==DONT) {

    if (data == TELOPT_ECHO) {
      mode = 0;
      return;
    }

    if (data == TELOPT_EOR) {
      do_eor = 0;
      mode = 0;
      return;
    }

    if (data == TELOPT_MXP) {
      mxp = 0;
      mode = 0;
      return;
    }

    /* IAC WONT foo */
    mode = 0;
    return;
  }
}

int msi::player::read(commandhandler *pl) {
  if (socket->get_dead())
    return -2;
  
  last_keepalive = now;
  
  while (1) {
    char data[500];
    int rval = socket->read(data, 500);
    if (rval == -1 && (errno == EWOULDBLOCK || errno == EAGAIN)) {
      flush();
      return -1;
    }
    if (rval == -1 || rval == 0)
      return -2;
    
    for (int i=0;i<rval;i++) {
      if (rawmode) /* in raw mode, don't do any telnet processing */
	datah(data[i], pl);
      else
	tstack(data[i], pl);
    }
  }

  flush();

  return 0;
}
#ifndef NOCOMPRESS
void msi::player::compress_on()
{
  if (!will_compress)
    return;

  if (compress)
    return;

  if (will_compress == 2) {
    char start[5] = {IAC, SB, TELOPT_COMPRESS2, IAC, SE};
    rawsend(start, 5);
  } else {
    char start[5] = {IAC, SB, TELOPT_COMPRESS, WILL, SE};
    rawsend(start, 5);
  }
  
  deflate = new z_stream;
  deflate->zalloc = zlib_alloc;
  deflate->zfree = zlib_free;
  deflate->opaque = NULL;
  deflate->next_in = small_outbuf;
  deflate->next_out = small_outbuf;
  deflate->avail_out = SMALL_BUFSIZE;
  deflate->avail_in = 0;
  
  if (int derr = deflateInit(deflate, Z_DEFAULT_COMPRESSION))
    log(PFL_SEEINFO, LEV_ADMIRAL, "compress", "deflateInit() returned %d.", derr);
  else
    compress = 1;
}

void msi::player::compress_off()
{
  if (deflate && compress) {
    int derr;
    int pending = deflate->avail_out;
    deflate->next_out = small_outbuf;
    if ((derr = ::deflate(deflate, Z_FINISH)) != Z_STREAM_END) {
      log(PFL_SEEINFO, LEV_ADMIRAL, "compress", "deflate(..., Z_FINISH) broke with %i!", derr);
    }
    pending -= deflate->avail_out;
    if (pending)
      socket->write((const char *)small_outbuf, pending);

    int z;
    z = deflateEnd(deflate);
    if (z!=Z_OK) {
      /* hope this is ok */
      log(PFL_SEEINFO, LEV_ADMIRAL, "compress", "deflateEnd() broke with %i!", z);
    }
    delete deflate;
    deflate = 0;
    compress = 0;
  }
}
#endif
msi::player::~player() {
  flush();
#ifndef NOCOMPRESS
  compress_off();
#endif
  delete socket;
}


msi::player::player(int fd) {
  blank();

  socket = new Socket();
  socket->connect(fd);
}

msi::player::player() {
  blank();
}

void msi::player::noecho(bool s)
{
  if (s != last_noecho) {
    last_noecho = s;

    if (s) {
      char e[] = { IAC, WILL, TELOPT_ECHO };
      rawsend(e, sizeof e);
    } else {
      char e[] = { IAC, WONT, TELOPT_ECHO };
      rawsend(e, sizeof e);
    }
  }
}

void msi::player::blank()
{
  buffer = "";

  cursor = 0;
  trunc = 0;
  newlinechar = 0;
  thingy_from = msi::tNone;
      
  do_eor = 0;
  rawmode = 0;
  row = 0;
  col = 0;
  term = "";
  
  last_noecho = 0;
  mxp = 0;
  
#ifndef NOCOMPRESS
  rawbytes = 0;
  uncbytes = 0;
  combytes = 0;
  deflate = 0;
  compress = 0;
  will_compress = 0;
#endif
  
  mplex = 0;
  
  se_str = "";
  mode = 0;
  se_mode = 0;
  
  titlebar = "";
  
  last_keepalive=time(0);
  
  mxp_version = "";
  
  esc = 0;
  lineeditor = 0;

  secure = secure_locked = 0;

  nosend = 0;
}