Browse Source

Prevent crash whith tinyfuge due to NULL terminal type. Also use gofmt to standardize the file's layout.

Beoran 8 years ago
parent
commit
ff341f6b2c
2 changed files with 789 additions and 743 deletions
  1. 249 184
      server/setuptelnet.go
  2. 540 559
      telnet/telnet.go

+ 249 - 184
server/setuptelnet.go

@@ -6,219 +6,284 @@ import "github.com/beoran/woe/telnet"
 import "strings"
 import "strconv"
 
+/* This file contains telnet setup helpers for the client. */
 
+// generic negotiation
 
-/* This file contains telnet setup helpers for the client. */
+func (me *Client) SetupNegotiate(millis int, command byte, option byte, yes_event telnet.EventType, no_event telnet.EventType) (bool, telnet.Event) {
+	me.telnet.TelnetSendNegotiate(command, option)
+	tev, timeout, close := me.TryReadEvent(millis)
 
+	if tev == nil || timeout || close {
+		monolog.Info("Timeout or close in TryReadEvent")
+		return false, nil
+	}
 
-// generic negotiation
+	evtype := telnet.EventTypeOf(tev)
+
+	if evtype == no_event {
+		monolog.Info("Negative event no_event %v %v %v", tev, evtype, no_event)
+		return false, tev
+	}
 
-func (me * Client) SetupNegotiate(millis int, command byte, option byte, yes_event telnet.EventType, no_event telnet.EventType) (bool, telnet.Event) {
-    me.telnet.TelnetSendNegotiate(command, option)
-    tev, timeout, close := me.TryReadEvent(millis)
-
-    if tev == nil || timeout || close {
-        monolog.Info("Timeout or close in TryReadEvent")
-        return false, nil
-    }
-    
-    evtype := telnet.EventTypeOf(tev)
-    
-    if evtype == no_event {
-        monolog.Info("Negative event no_event %v %v %v", tev, evtype, no_event)
-        return false, tev
-    }
-    
-    if evtype != yes_event {
-        monolog.Info("Unexpected event yes_event %v %v %v", tev, evtype, yes_event)
-        return false, tev
-    }
-    
-    return true, tev
+	if evtype != yes_event {
+		monolog.Info("Unexpected event yes_event %v %v %v", tev, evtype, yes_event)
+		return false, tev
+	}
+
+	if tev == nil {
+		return false, tev
+	}
+
+	return true, tev
 }
 
-  
-// Negotiate COMPRESS2 support
-func (me * Client) SetupCompress2() telnet.Event {
-    ok, tev := me.SetupNegotiate(1000, t.TELNET_WILL, t.TELNET_TELOPT_COMPRESS2, t.TELNET_DO_EVENT, t.TELNET_DONT_EVENT)
-    if (!ok) {
-        return tev
-    } 
-     
-    me.telnet.TelnetBeginCompress2()
-    monolog.Info("Client #{@id} started COMPRESS2 compression")
-    me.info.compress2 = true
-    return tev
+// Negotiate BINARY support, for sending UTF-8. This is largely a courtesy to the cliennt,
+// since woe will send UTF-8 anyway.
+func (me *Client) SetupBinary() telnet.Event {
+	ok, tev := me.SetupNegotiate(1000, t.TELNET_WILL, t.TELNET_TELOPT_BINARY, t.TELNET_DO_EVENT, t.TELNET_DONT_EVENT)
+	if !ok {
+		me.info.binary = false
+		return tev
+	}
+
+	monolog.Info("Client #{@id} accepts BINARY")
+	me.info.binary = true
+	return tev
 }
 
+// negotiate suppress go ahead
+func (me *Client) SetupSupressGA() telnet.Event {
+	ok, tev := me.SetupNegotiate(1000, t.TELNET_WILL, t.TELNET_TELOPT_SGA, t.TELNET_DO_EVENT, t.TELNET_DONT_EVENT)
+	if !ok {
+		me.info.binary = false
+		return tev
+	}
+
+	monolog.Info("Client #{@id} will suppress GA")
+	me.info.sga = true
+	return tev
+
+}
+
+// Negotiate COMPRESS2 support
+func (me *Client) SetupCompress2() telnet.Event {
+	ok, tev := me.SetupNegotiate(1000, t.TELNET_WILL, t.TELNET_TELOPT_COMPRESS2, t.TELNET_DO_EVENT, t.TELNET_DONT_EVENT)
+	if !ok {
+		return tev
+	}
+
+	me.telnet.TelnetBeginCompress2()
+	monolog.Info("Client #{@id} started COMPRESS2 compression")
+	me.info.compress2 = true
+	return tev
+}
 
 // Negotiate NAWS (window size) support
-func (me * Client) SetupNAWS() telnet.Event {  
-    ok, tev := me.SetupNegotiate(1000, t.TELNET_DO, t.TELNET_TELOPT_NAWS, t.TELNET_WILL_EVENT, t.TELNET_WONT_EVENT)
-    if (!ok) {
-        return tev
-    } 
-    
-    tev2, _, _ := me.TryReadEvent(1000)
-    if (tev2 == nil) || (!telnet.IsEventType(tev2, t.TELNET_NAWS_EVENT)) {
-        return tev2
-    }
-    
-    nawsevent, ok := tev2.(*telnet.NAWSEvent)
-    if ok {
-        me.info.w = nawsevent.W
-        me.info.h = nawsevent.H
-        monolog.Info("Client %d window size #{%d}x#{%d}", me.id, me.info.w, me.info.h) 
-        me.info.naws     = true
-    }
-    return nil
+func (me *Client) SetupNAWS() telnet.Event {
+	ok, tev := me.SetupNegotiate(1000, t.TELNET_DO, t.TELNET_TELOPT_NAWS, t.TELNET_WILL_EVENT, t.TELNET_WONT_EVENT)
+	if !ok {
+		return tev
+	}
+
+	tev2, _, _ := me.TryReadEvent(1000)
+	if (tev2 == nil) || (!telnet.IsEventType(tev2, t.TELNET_NAWS_EVENT)) {
+		return tev2
+	}
+
+	nawsevent, ok := tev2.(*telnet.NAWSEvent)
+	if ok {
+		me.info.w = nawsevent.W
+		me.info.h = nawsevent.H
+		monolog.Info("Client %d window size #{%d}x#{%d}", me.id, me.info.w, me.info.h)
+		me.info.naws = true
+	}
+	return nil
 }
- 
-func (me * Client) SetupMSSP() telnet.Event {
-    ok, tev := me.SetupNegotiate(1000, t.TELNET_WILL, t.TELNET_TELOPT_MSSP, t.TELNET_DO_EVENT, t.TELNET_DONT_EVENT)
-    if (!ok) {
-        return tev
-    } 
-    me.telnet.TelnetSendMSSP(MSSP)
-    monolog.Info("Client %d accepts MSSP", me.id) 
-    me.info.mssp = true
-    return nil
+
+func (me *Client) SetupMSSP() telnet.Event {
+	ok, tev := me.SetupNegotiate(1000, t.TELNET_WILL, t.TELNET_TELOPT_MSSP, t.TELNET_DO_EVENT, t.TELNET_DONT_EVENT)
+	if !ok {
+		return tev
+	}
+	me.telnet.TelnetSendMSSP(MSSP)
+	monolog.Info("Client %d accepts MSSP", me.id)
+	me.info.mssp = true
+	return nil
 }
- 
+
 // Check for MXP (html-like) support (but don't implement it yet)
-func (me * Client) SetupMXP() telnet.Event { 
-
-    ok, tev := me.SetupNegotiate(1000, t.TELNET_DO, t.TELNET_TELOPT_MXP, t.TELNET_WILL_EVENT, t.TELNET_WONT_EVENT)
-    if (!ok) {
-        return tev
-    } 
-    monolog.Info("Client %d accepts MXP", me.id) 
-    me.info.mxp = true
-    return nil
+func (me *Client) SetupMXP() telnet.Event {
+
+	ok, tev := me.SetupNegotiate(1000, t.TELNET_DO, t.TELNET_TELOPT_MXP, t.TELNET_WILL_EVENT, t.TELNET_WONT_EVENT)
+	if !ok {
+		return tev
+	}
+	monolog.Info("Client %d accepts MXP", me.id)
+	me.info.mxp = true
+	return nil
 }
 
 // Check for MSP (sound) support (but don't implement it yet)
-func (me * Client) SetupMSP() telnet.Event { 
-
-    ok, tev := me.SetupNegotiate(1000, t.TELNET_DO, t.TELNET_TELOPT_MSP, t.TELNET_WILL_EVENT, t.TELNET_WONT_EVENT)
-    if (!ok) {
-        return tev
-    } 
-    monolog.Info("Client %d accepts MSP", me.id) 
-    me.info.msp = true
-    return nil
+func (me *Client) SetupMSP() telnet.Event {
+
+	ok, tev := me.SetupNegotiate(1000, t.TELNET_DO, t.TELNET_TELOPT_MSP, t.TELNET_WILL_EVENT, t.TELNET_WONT_EVENT)
+	if !ok {
+		return tev
+	}
+	monolog.Info("Client %d accepts MSP", me.id)
+	me.info.msp = true
+	return nil
 }
 
 // Check for MSDP (two way MSSP) support (but don't implement it yet)
-func (me * Client) SetupMSDP() telnet.Event { 
-
-    ok, tev := me.SetupNegotiate(1000, t.TELNET_WILL, t.TELNET_TELOPT_MSDP, t.TELNET_DO_EVENT, t.TELNET_DONT_EVENT)
-    if (!ok) {
-        return tev
-    } 
-    monolog.Info("Client %d accepts MSDP", me.id) 
-    me.info.msdp = true
-    return nil
+func (me *Client) SetupMSDP() telnet.Event {
+
+	ok, tev := me.SetupNegotiate(1000, t.TELNET_WILL, t.TELNET_TELOPT_MSDP, t.TELNET_DO_EVENT, t.TELNET_DONT_EVENT)
+	if !ok {
+		return tev
+	}
+	monolog.Info("Client %d accepts MSDP", me.id)
+	me.info.msdp = true
+	return nil
 }
 
-func (me * Client) HasTerminal(name string) bool {
-    monolog.Debug("Client %d supports terminals? %s %v", me.id, name, me.info.terminals)
-    for index := range me.info.terminals {
-        if (me.info.terminals[index] == name) {
-            return true
-        }
-    }
-    return false
+func (me *Client) HasTerminal(name string) bool {
+	monolog.Debug("Client %d supports terminals? %s %v", me.id, name, me.info.terminals)
+	for index := range me.info.terminals {
+		if me.info.terminals[index] == name {
+			return true
+		}
+	}
+	return false
 }
 
+// Negotiate MTTS/TTYPE (TERMINAL TYPE) support
+func (me *Client) SetupTType() telnet.Event {
+	me.info.terminals = nil
+	ok, tev := me.SetupNegotiate(1000, t.TELNET_DO, t.TELNET_TELOPT_TTYPE, t.TELNET_WILL_EVENT, t.TELNET_WONT_EVENT)
+	if !ok {
+		return tev
+	}
 
+	var last string = "none"
+	var now string = ""
 
+	for last != now {
+		last = now
+		me.telnet.TelnetTTypeSend()
+		var tev2 telnet.Event = nil
+		// Some clients (like KildClient, but not TinTin or telnet),
+		// insist on spamming useless NUL characters
+		// here... So we have to retry a few times to get a ttype_is
+		// throwing away any undesirable junk in between.
+	GET_TTYPE:
+		for index := 0; index < 3; index++ {
+			tev2, _, _ = me.TryReadEvent(1000)
+			if tev2 != nil {
+				etyp := telnet.EventTypeOf(tev2)
+				monolog.Info("Waiting for TTYPE: %T %v %d", tev2, tev2, etyp)
+				if telnet.IsEventType(tev2, t.TELNET_TTYPE_EVENT) {
+					monolog.Info("TTYPE received: %T %v %d", tev2, tev2, etyp)
+					break GET_TTYPE
+				}
+			} else { // and some clients don't respond, even
+				monolog.Info("Waiting for TTYPE: %T", tev2)
+			}
+		}
 
-// Negotiate MTTS/TTYPE (TERMINAL TYPE) support
-func (me * Client)  SetupTType() telnet.Event {
-    me.info.terminals = nil
-    ok, tev := me.SetupNegotiate(1000, t.TELNET_DO, t.TELNET_TELOPT_TTYPE, t.TELNET_WILL_EVENT, t.TELNET_WONT_EVENT)
-    if (!ok) {
-        return tev
-    }
-        
-    var last string = "none"
-    var now  string = ""
-    
-    for last != now {
-        last = now
-        me.telnet.TelnetTTypeSend()
-        var tev2 telnet.Event = nil
-        // Some clients (like KildClient, but not TinTin or telnet), 
-        // insist on spamming useless NUL characters
-        // here... So we have to retry a few times to get a ttype_is
-        // throwing away any undesirable junk in between.
-        GET_TTYPE: for index := 0 ; index < 3 ; index++ {
-            tev2, _, _ = me.TryReadEvent(1000)
-            etyp := telnet.EventTypeOf(tev2)
-            monolog.Info("Waiting for TTYPE: %T %v %d", tev2, tev2, etyp)
-            if tev2 != nil && telnet.IsEventType(tev2, t.TELNET_TTYPE_EVENT) {
-                monolog.Info("TTYPE received: %T %v %d", tev2, tev2, etyp)
-                break GET_TTYPE
-            }
-        }
-        
-        if tev2 == nil || !telnet.IsEventType(tev2, t.TELNET_TTYPE_EVENT) {
-            etyp := telnet.EventTypeOf(tev2)
-            monolog.Warning("Received no TTYPE: %T %v %d", tev2, tev2, etyp)
-            return tev2
-        }
-        
-        ttypeevent := tev2.(*telnet.TTypeEvent)
-        now = ttypeevent.Name
-        if (!me.HasTerminal(now)) {
-            me.info.terminals = append(me.info.terminals, now)
-        }
-        me.info.terminal = now
-    }
-    
-    monolog.Info("Client %d supports terminals %v", me.id, me.info.terminals)
-    monolog.Info("Client %d active terminal %v", me.id, me.info.terminal)
-
-    //  MTTS support
-    for i := range me.info.terminals {
-        term := me.info.terminals[i]
-        monolog.Info("Checking MTTS support: %s", term)
-        if strings.HasPrefix(term, "MTTS ") {
-            // it's an mtts terminal
-            strnum := strings.TrimPrefix(term, "MTTS ")
-            num, err := strconv.Atoi(strnum)
-            if err == nil {
-                me.info.mtts = num
-                monolog.Info("Client %d supports mtts %d", me.id, me.info.mtts)                
-            } else {
-                monolog.Warning("Client %d could not parse mtts %s %v", me.id, strnum, err)
-            }
-        }
-    }
-    me.info.ttype = true
-    return nil
-}
+		if tev2 == nil || !telnet.IsEventType(tev2, t.TELNET_TTYPE_EVENT) {
+			etyp := telnet.EventTypeOf(tev2)
+			monolog.Warning("Received no TTYPE: %T %v %d", tev2, tev2, etyp)
+			return tev2
+		}
+
+		ttypeevent := tev2.(*telnet.TTypeEvent)
+		now = ttypeevent.Name
+		if !me.HasTerminal(now) {
+			me.info.terminals = append(me.info.terminals, now)
+		}
+		me.info.terminal = now
+	}
 
-func (me * Client) SetupTelnet() {
-    for {
-      tev, _, _ := me.TryReadEvent(500)
-      if tev != nil {
-        monolog.Info("Client %d telnet setup received: %v", me.id, tev)
-      } else {
-        monolog.Info("Client %d no telnet setup received", me.id)
-        break
-      }
-    }
-    me.SetupMSSP()
-    // me.SetupCompress2
-    me.SetupNAWS()
-    me.SetupTType()
-    me.SetupMXP()
-    me.SetupMSP()
-    me.SetupMSDP()
-    // me.ColorTest()
+	monolog.Info("Client %d supports terminals %v", me.id, me.info.terminals)
+	monolog.Info("Client %d active terminal %v", me.id, me.info.terminal)
+
+	//  MTTS support
+	for i := range me.info.terminals {
+		term := me.info.terminals[i]
+		monolog.Info("Checking MTTS support: %s", term)
+		if strings.HasPrefix(term, "MTTS ") {
+			// it's an mtts terminal
+			strnum := strings.TrimPrefix(term, "MTTS ")
+			num, err := strconv.Atoi(strnum)
+			if err == nil {
+				me.info.mtts = num
+				monolog.Info("Client %d supports mtts %d", me.id, me.info.mtts)
+			} else {
+				monolog.Warning("Client %d could not parse mtts %s %v", me.id, strnum, err)
+			}
+		}
+	}
+	me.info.ttype = true
+	return nil
 }
 
+/*
+Although most clients don't do this, some clients, like tinyfuge actively initiate telnet setup.
+Deal with that first, then do our own setup. It seems like tinyfuge is "waiting" for something
+before performing the setup, which messes up TryReadEvent...
+*/
+
+func (me *Client) ReadTelnetSetup() {
+	/* First send a NOP to prod the client into activity. This shouldn't be neccesary,
+	but some clients, in particular Tinyfugue waits for server activity before it sends it's own
+	telnet setup messages. If the NOP is not sent, those setup messages will then end up messing up the
+	telnet setup the WOE server is trying to do. Arguably this should perhaps be an AYT, but a NOP will
+	hopefully do the least damage.
+	*/
+	msg := []byte{telnet.TELNET_IAC, telnet.TELNET_NOP}
+	me.telnet.SendRaw(msg)
+
+	for {
+		// wait for incoming events.
+		event, _, _ := me.TryReadEvent(500)
+		if event == nil {
+			monolog.Info("Client %d no telnet setup received", me.id)
+			return
+		}
+
+		monolog.Info("Client %d telnet setup received: %v", me.id, event)
 
+		switch event := event.(type) {
+		case *telnet.DataEvent:
+			monolog.Info("Unexpected DATA event %T : %d.", event, len(event.Data))
+			// should do something with the data, maybe?
+			return
+		case *telnet.NAWSEvent:
+			monolog.Info("Received Telnet NAWS event %T.", event)
+			me.HandleNAWSEvent(event)
 
+		case *telnet.WillEvent:
+			monolog.Info("Received Telnet WILL event %T.", event)
+			// XXX do something with it!
+
+		default:
+			monolog.Info("Unexpected event in setup phase %T : %v. Ignored for now.", event, event)
+		}
+	}
+}
+
+func (me *Client) SetupTelnet() {
+	me.ReadTelnetSetup()
+	me.SetupSupressGA()
+	// me.SetupBinary()
+	me.SetupMSSP()
+	// me.SetupCompress2
+	me.SetupNAWS()
+	me.SetupTType()
+	me.SetupMXP()
+	me.SetupMSP()
+	me.SetupMSDP()
+	// me.ColorTest()
+}

+ 540 - 559
telnet/telnet.go

@@ -7,726 +7,707 @@ import "fmt"
 import "compress/zlib"
 import "github.com/beoran/woe/monolog"
 
-
 // This Telnet module implements a subset of the Telnet protocol.
 
 // Telnet states
 type TelnetState int
 
 const (
-    data_state TelnetState  = iota
-    iac_state               = iota
-    will_state              = iota
-    wont_state              = iota
-    do_state                = iota
-    dont_state              = iota
-    sb_state                = iota
-    sb_data_state           = iota
-    sb_data_iac_state       = iota
+	data_state        TelnetState = iota
+	iac_state                     = iota
+	will_state                    = iota
+	wont_state                    = iota
+	do_state                      = iota
+	dont_state                    = iota
+	sb_state                      = iota
+	sb_data_state                 = iota
+	sb_data_iac_state             = iota
 )
 
 // Telnet event type constants
 type EventType int
 
 const (
-    TELNET_DATA_EVENT           EventType =  iota
-    TELNET_NAWS_EVENT           EventType =  iota
-    TELNET_TTYPE_EVENT          EventType =  iota
-    TELNET_SUBNEGOTIATE_EVENT   EventType =  iota
-    TELNET_IAC_EVENT            EventType =  iota
-    TELNET_COMPRESS_EVENT       EventType =  iota
-    TELNET_ENVIRONMENT_EVENT    EventType =  iota
-    TELNET_MSSP_EVENT           EventType =  iota
-    TELNET_ZMP_EVENT            EventType =  iota
-    TELNET_WILL_EVENT           EventType =  iota
-    TELNET_WONT_EVENT           EventType =  iota
-    TELNET_DO_EVENT             EventType =  iota
-    TELNET_DONT_EVENT           EventType =  iota
-    TELNET_ERROR_EVENT          EventType =  iota
-    TELNET_UNKNOWN_EVENT        EventType =  iota
+	TELNET_DATA_EVENT         EventType = iota
+	TELNET_NAWS_EVENT         EventType = iota
+	TELNET_TTYPE_EVENT        EventType = iota
+	TELNET_SUBNEGOTIATE_EVENT EventType = iota
+	TELNET_IAC_EVENT          EventType = iota
+	TELNET_COMPRESS_EVENT     EventType = iota
+	TELNET_ENVIRONMENT_EVENT  EventType = iota
+	TELNET_MSSP_EVENT         EventType = iota
+	TELNET_ZMP_EVENT          EventType = iota
+	TELNET_WILL_EVENT         EventType = iota
+	TELNET_WONT_EVENT         EventType = iota
+	TELNET_DO_EVENT           EventType = iota
+	TELNET_DONT_EVENT         EventType = iota
+	TELNET_ERROR_EVENT        EventType = iota
+	TELNET_UNKNOWN_EVENT      EventType = iota
+	TELNET_NIL_EVENT          EventType = iota
 )
 
 // Telnet event types
 
 type Event interface {
-    Type() EventType
+	Type() EventType
 }
 
 type DataEvent struct {
-    Data [] byte
+	Data []byte
 }
 
-func (me DataEvent) Type() EventType { return TELNET_DATA_EVENT; }
+func (me DataEvent) Type() EventType { return TELNET_DATA_EVENT }
 
 type NAWSEvent struct {
-    W   int
-    H   int
+	W int
+	H int
 }
 
-func (me NAWSEvent) Type() EventType { return TELNET_NAWS_EVENT; }
-
+func (me NAWSEvent) Type() EventType { return TELNET_NAWS_EVENT }
 
 type TTypeEvent struct {
-    Telopt  byte
-    Name    string
+	Telopt byte
+	Name   string
 }
 
-func (me TTypeEvent) Type() EventType { return TELNET_TTYPE_EVENT; }
-
+func (me TTypeEvent) Type() EventType { return TELNET_TTYPE_EVENT }
 
 type SubnegotiateEvent struct {
-    Telopt    byte
-    Buffer [] byte
+	Telopt byte
+	Buffer []byte
 }
 
-func (me SubnegotiateEvent) Type() EventType { return TELNET_SUBNEGOTIATE_EVENT; }
-
+func (me SubnegotiateEvent) Type() EventType { return TELNET_SUBNEGOTIATE_EVENT }
 
 type IACEvent struct {
-    Telopt    byte
+	Telopt byte
 }
 
-func (me IACEvent) Type() EventType { return TELNET_IAC_EVENT; }
-
+func (me IACEvent) Type() EventType { return TELNET_IAC_EVENT }
 
 type CompressEvent struct {
-    Compress  bool
+	Compress bool
 }
 
-func (me CompressEvent) Type() EventType { return TELNET_COMPRESS_EVENT; }
-
+func (me CompressEvent) Type() EventType { return TELNET_COMPRESS_EVENT }
 
 // Storage for environment values
 type Environment struct {
-    Type byte
-    Value string
+	Type  byte
+	Value string
 }
 
-
 type EnvironmentEvent struct {
-    Telopt    byte
-    Vars      [] Environment
+	Telopt byte
+	Vars   []Environment
 }
 
-func (me EnvironmentEvent) Type() EventType { return TELNET_ENVIRONMENT_EVENT; }
-
+func (me EnvironmentEvent) Type() EventType { return TELNET_ENVIRONMENT_EVENT }
 
 type MSSPEvent struct {
-    Telopt    byte
-    Vars      map[string] string
+	Telopt byte
+	Vars   map[string]string
 }
 
-func (me MSSPEvent) Type() EventType { return TELNET_MSSP_EVENT; }
-
+func (me MSSPEvent) Type() EventType { return TELNET_MSSP_EVENT }
 
 type ZMPEvent struct {
-    Vars      []string
+	Vars []string
 }
 
-func (me ZMPEvent) Type() EventType { return TELNET_ZMP_EVENT; }
+func (me ZMPEvent) Type() EventType { return TELNET_ZMP_EVENT }
 
 type WillEvent struct {
-    Telopt byte
+	Telopt byte
 }
 
-func (me WillEvent) Type() EventType { return TELNET_WILL_EVENT; }
+func (me WillEvent) Type() EventType { return TELNET_WILL_EVENT }
 
 type WontEvent struct {
-    Telopt byte
+	Telopt byte
 }
 
-func (me WontEvent) Type() EventType { return TELNET_WONT_EVENT; }
-
+func (me WontEvent) Type() EventType { return TELNET_WONT_EVENT }
 
 type DoEvent struct {
-    Telopt byte
+	Telopt byte
 }
 
-func (me DoEvent) Type() EventType { return TELNET_DO_EVENT; }
+func (me DoEvent) Type() EventType { return TELNET_DO_EVENT }
 
 type DontEvent struct {
-    Telopt byte
+	Telopt byte
 }
 
-func (me DontEvent) Type() EventType { return TELNET_DONT_EVENT; }
-
+func (me DontEvent) Type() EventType { return TELNET_DONT_EVENT }
 
 // For protocol errors.
 type ErrorEvent struct {
-    error string
+	error string
 }
 
-func (me ErrorEvent) Type() EventType { return TELNET_ERROR_EVENT; }
-
+func (me ErrorEvent) Type() EventType { return TELNET_ERROR_EVENT }
 
 // Returns the numerical event type of an event. Useful for direct comparison.
 func EventTypeOf(event Event) EventType {
-    return event.Type()
+	if event == nil {
+		return TELNET_NIL_EVENT
+	}
+	return event.Type()
 }
 
 // Returns true if the event is of the given type, or false if not
 func IsEventType(event Event, typ EventType) bool {
-    return EventTypeOf(event) == typ;
+	return EventTypeOf(event) == typ
 }
 
-type EventChannel chan(Event)
-
+type EventChannel chan (Event)
 
 type Telopt struct {
-    telopt byte
-    us     byte
-    him    byte
-}
-    
-type Telnet struct { 
-  Events            EventChannel
-  ToClient          chan([]byte)
-  telopts map[byte] Telopt
-  state             TelnetState
-  compress          bool
-  zwriter           zlib.Writer
-  zreader           io.ReadCloser
-  buffer          []byte
-  sb_telopt         byte
-}
-
-func New() (telnet * Telnet) {
-    events     := make(EventChannel, 64)
-    toclient   := make(chan([]byte), 64)
-    telopts    := make (map[byte] Telopt)
-    state      := data_state
-    compress   := false
-    var zwriter zlib.Writer
-    var zreader io.ReadCloser
-    var buffer []byte = nil
-    sb_telopt  := byte(0)
-    telnet      = &Telnet { events, toclient, telopts, state, compress, zwriter, zreader, buffer, sb_telopt }
-    return telnet
+	telopt byte
+	us     byte
+	him    byte
+}
+
+type Telnet struct {
+	Events    EventChannel
+	ToClient  chan ([]byte)
+	telopts   map[byte]Telopt
+	state     TelnetState
+	compress  bool
+	zwriter   zlib.Writer
+	zreader   io.ReadCloser
+	buffer    []byte
+	sb_telopt byte
+}
+
+func New() (telnet *Telnet) {
+	events := make(EventChannel, 64)
+	toclient := make(chan ([]byte), 64)
+	telopts := make(map[byte]Telopt)
+	state := data_state
+	compress := false
+	var zwriter zlib.Writer
+	var zreader io.ReadCloser
+	var buffer []byte = nil
+	sb_telopt := byte(0)
+	telnet = &Telnet{events, toclient, telopts, state, compress, zwriter, zreader, buffer, sb_telopt}
+	return telnet
 }
 
 // Starts compresssion
-func (me * Telnet) StartCompression() {
-    // XXX implement compression.
-    // var zwbuf  bytes.Buffer
-    // me.zwriter = zlib.NewWriter(&zwbuf);
+func (me *Telnet) StartCompression() {
+	// XXX implement compression.
+	// var zwbuf  bytes.Buffer
+	// me.zwriter = zlib.NewWriter(&zwbuf);
 }
-  
+
 // Closes the telnet connection, send last compressed data if needed.
-func (me * Telnet) Close() { 
-    if me.compress  {
-        me.zwriter.Close()
-        me.zreader.Close()    
-    }
-}
-
-// Filters raw text, only compressing it if needed. 
-func (me * Telnet) SendRaw(in []byte) {
-    // XXX Handle compression here later
-    me.ToClient <- in
-} 
-
-// Filters text, escaping IAC bytes. 
-func (me * Telnet) SendEscaped(in []byte) {
-    buffer := make([]byte, len(in) * 2, len(in) * 2) 
-    outdex := 0
-    /* Double IAC characters to escape them. */
-    for index := 0; index < len(in) ; index++ {
-        now := in[index]
-        if now == TELNET_IAC {
-            buffer[outdex] = TELNET_IAC; 
-            outdex++;    
-        }
-        buffer[outdex] = now;
-        outdex++;
-    }
-    me.SendRaw(buffer)
-} 
+func (me *Telnet) Close() {
+	if me.compress {
+		me.zwriter.Close()
+		me.zreader.Close()
+	}
+}
+
+// Filters raw text, only compressing it if needed.
+func (me *Telnet) SendRaw(in []byte) {
+	// XXX Handle compression here later
+	me.ToClient <- in
+}
+
+// Filters text, escaping IAC bytes.
+func (me *Telnet) SendEscaped(in []byte) {
+	buffer := make([]byte, len(in)*2, len(in)*2)
+	outdex := 0
+	/* Double IAC characters to escape them. */
+	for index := 0; index < len(in); index++ {
+		now := in[index]
+		if now == TELNET_IAC {
+			buffer[outdex] = TELNET_IAC
+			outdex++
+		}
+		buffer[outdex] = now
+		outdex++
+	}
+	me.SendRaw(buffer)
+}
 
 // Send negotiation bytes
-func (me * Telnet) SendNegotiate(cmd byte, telopt byte) {
-    buffer      := make([]byte, 3)
-    buffer[0]    = TELNET_IAC
-    buffer[1]    = cmd
-    buffer[2]    = telopt
-    me.SendRaw(buffer)
+func (me *Telnet) SendNegotiate(cmd byte, telopt byte) {
+	buffer := make([]byte, 3)
+	buffer[0] = TELNET_IAC
+	buffer[1] = cmd
+	buffer[2] = telopt
+	me.SendRaw(buffer)
 }
 
-func (me * Telnet) SendEvent(event Event) {
-    me.Events <- event
+func (me *Telnet) SendEvent(event Event) {
+	me.Events <- event
 }
-   
+
 // Parse a subnegotiation buffer for a naws event
-func (me * Telnet) SubnegotiateNAWS(buffer []byte) {
-    // Some clients, like Gnome-Mud can't even get this right. Grrr!
-    if buffer == nil || len(buffer) != 4 {
-      monolog.Warning("Bad NAWS negotiation: #{buffer}")
-      return
-    }
-    var w int   = (int(buffer[0]) << 8) + int(buffer[1])
-    var h int   = (int(buffer[2]) << 8) + int(buffer[3])
-    me.SendEvent(&NAWSEvent{w, h})
+func (me *Telnet) SubnegotiateNAWS(buffer []byte) {
+	// Some clients, like Gnome-Mud can't even get this right. Grrr!
+	if buffer == nil || len(buffer) != 4 {
+		monolog.Warning("Bad NAWS negotiation: #{buffer}")
+		return
+	}
+	var w int = (int(buffer[0]) << 8) + int(buffer[1])
+	var h int = (int(buffer[2]) << 8) + int(buffer[3])
+	me.SendEvent(&NAWSEvent{w, h})
 }
 
 // process an ENVIRON/NEW-ENVIRON subnegotiation buffer
-func (me * Telnet) SubnegotiateEnviron(buffer []byte) {
-    var vars []Environment
-    var cmd []byte
-    fb   := buffer[0]  
-    // First byte must be a valid command 
-    if fb != TELNET_ENVIRON_SEND && fb != TELNET_ENVIRON_IS && fb != TELNET_ENVIRON_INFO {
-      monolog.Warning("telopt environment subneg command not valid")
-    }
-    
-    cmd = append(cmd, fb)   
-    
-    if len(buffer) == 1 { 
-      me.SendEvent(&EnvironmentEvent{fb, vars})
-      return
-    }
-        
-    // Second byte must be VAR or USERVAR, if present
-    sb := buffer[1]
-    if sb != TELNET_ENVIRON_VAR && fb != TELNET_ENVIRON_USERVAR {
-      monolog.Warning("telopt environment subneg missing variable type")
-      return
-    }
-    
-    // ensure last byte is not an escape byte (makes parsing later easier) 
-    lb := buffer[len(buffer) - 1]
-    if lb == TELNET_ENVIRON_ESC {
-      monolog.Warning("telopt environment subneg ends with ESC")
-      return
-    }
-
-/* XXX : not implemented yet
-    var variable * Environment = nil
-    index           := 1
-    escape          := false
-    
-    for index := 1 ; index < len(buffer) ; index++ {
-      c := buffer[index]  
-      switch c {
-        case TELNET_ENVIRON_VAR: 
-            fallthrough
-        case TELNET_ENVIRON_VALUE:
-            fallthrough
-        case TELNET_ENVIRON_USERVAR:
-            if escape {
-                escape = false
-                variable.Value  = append(variable.Value, c)
-            } else if (variable != nil) {
-                vars            = append(vars, variable)
-                variable        = new(Environment)
-                variable.Type   = c
-            } else {
-                variable        = new(Environment)
-                variable.Type   = c
-            }
-      case TELNET_ENVIRON_ESC:
-        escape = true
-      default:
-        variable.Value = append(variable.Value, c)
-      }
-    }
-    // Finally send event
-    me.SendEvent(&EnvironmentEvent{fb, vars})
-*/
+func (me *Telnet) SubnegotiateEnviron(buffer []byte) {
+	var vars []Environment
+	var cmd []byte
+	fb := buffer[0]
+	// First byte must be a valid command
+	if fb != TELNET_ENVIRON_SEND && fb != TELNET_ENVIRON_IS && fb != TELNET_ENVIRON_INFO {
+		monolog.Warning("telopt environment subneg command not valid")
+	}
+
+	cmd = append(cmd, fb)
+
+	if len(buffer) == 1 {
+		me.SendEvent(&EnvironmentEvent{fb, vars})
+		return
+	}
+
+	// Second byte must be VAR or USERVAR, if present
+	sb := buffer[1]
+	if sb != TELNET_ENVIRON_VAR && fb != TELNET_ENVIRON_USERVAR {
+		monolog.Warning("telopt environment subneg missing variable type")
+		return
+	}
+
+	// ensure last byte is not an escape byte (makes parsing later easier)
+	lb := buffer[len(buffer)-1]
+	if lb == TELNET_ENVIRON_ESC {
+		monolog.Warning("telopt environment subneg ends with ESC")
+		return
+	}
+
+	/* XXX : not implemented yet
+	   var variable * Environment = nil
+	   index           := 1
+	   escape          := false
+
+	   for index := 1 ; index < len(buffer) ; index++ {
+	     c := buffer[index]
+	     switch c {
+	       case TELNET_ENVIRON_VAR:
+	           fallthrough
+	       case TELNET_ENVIRON_VALUE:
+	           fallthrough
+	       case TELNET_ENVIRON_USERVAR:
+	           if escape {
+	               escape = false
+	               variable.Value  = append(variable.Value, c)
+	           } else if (variable != nil) {
+	               vars            = append(vars, variable)
+	               variable        = new(Environment)
+	               variable.Type   = c
+	           } else {
+	               variable        = new(Environment)
+	               variable.Type   = c
+	           }
+	     case TELNET_ENVIRON_ESC:
+	       escape = true
+	     default:
+	       variable.Value = append(variable.Value, c)
+	     }
+	   }
+	   // Finally send event
+	   me.SendEvent(&EnvironmentEvent{fb, vars})
+	*/
 }
 
-
 const (
-    MSTATE_NONE = 0
-    MSTATE_VAR  = 1
-    MSTATE_VAL  = 2
+	MSTATE_NONE = 0
+	MSTATE_VAR  = 1
+	MSTATE_VAL  = 2
 )
 
 // process an MSSP subnegotiation buffer
-func (me * Telnet) SubnegotiateMSSP(buffer []byte) {
-    if len(buffer) < 1 {
-        return
-    }
-  
-    fb    := buffer[0]  
-    // first byte must be a valid command
-    if fb != TELNET_MSSP_VAR {
-        monolog.Warning("telopt MSSP subneg data not valid")
-        return
-    }
-  
-    variables := make(map[string] string)
-    var variable []byte
-    var value []byte
-    mstate := MSTATE_NONE
-    
-    for index := 0 ; index <  len(buffer) ; index ++ {
-        c     := buffer[index]
-        
-        switch c {
-            case TELNET_MSSP_VAR:
-            mstate = MSTATE_VAR
-            if mstate == MSTATE_VAR {
-                variables[string(variable)] = string(value)
-                variable = nil
-                value    = nil
-            }
-            case TELNET_MSSP_VAL:
-                mstate = MSTATE_VAL
-            default:
-                if mstate == MSTATE_VAL {
-                    variable = append(variable, c)
-                } else {
-                    value = append(value, c)
-                }  
-        }
-    }
-    me.SendEvent(&MSSPEvent{fb, variables})
-}
-
-
-// Parse ZMP command subnegotiation buffers 
-func (me * Telnet) SubnegotiateZMP(buffer []byte) {
-  var vars []string
-  var variable []byte
-  var b byte
-  for index := 0 ; index < len(buffer) ; index++ {
-      b = buffer[index]
-      if b == 0 {
-        vars     = append(vars, string(variable))
-        variable = nil
-      } else {
-        variable = append(variable, b)
-      }  
-  }
-  me.SendEvent(&ZMPEvent{vars})
+func (me *Telnet) SubnegotiateMSSP(buffer []byte) {
+	if len(buffer) < 1 {
+		return
+	}
+
+	fb := buffer[0]
+	// first byte must be a valid command
+	if fb != TELNET_MSSP_VAR {
+		monolog.Warning("telopt MSSP subneg data not valid")
+		return
+	}
+
+	variables := make(map[string]string)
+	var variable []byte
+	var value []byte
+	mstate := MSTATE_NONE
+
+	for index := 0; index < len(buffer); index++ {
+		c := buffer[index]
+
+		switch c {
+		case TELNET_MSSP_VAR:
+			mstate = MSTATE_VAR
+			if mstate == MSTATE_VAR {
+				variables[string(variable)] = string(value)
+				variable = nil
+				value = nil
+			}
+		case TELNET_MSSP_VAL:
+			mstate = MSTATE_VAL
+		default:
+			if mstate == MSTATE_VAL {
+				variable = append(variable, c)
+			} else {
+				value = append(value, c)
+			}
+		}
+	}
+	me.SendEvent(&MSSPEvent{fb, variables})
+}
+
+// Parse ZMP command subnegotiation buffers
+func (me *Telnet) SubnegotiateZMP(buffer []byte) {
+	var vars []string
+	var variable []byte
+	var b byte
+	for index := 0; index < len(buffer); index++ {
+		b = buffer[index]
+		if b == 0 {
+			vars = append(vars, string(variable))
+			variable = nil
+		} else {
+			variable = append(variable, b)
+		}
+	}
+	me.SendEvent(&ZMPEvent{vars})
 }
 
 // parse TERMINAL-TYPE command subnegotiation buffers
-func (me * Telnet) SubnegotiateTType(buffer []byte) {
-  // make sure request is not empty
-  if len(buffer) == 0 {
-    monolog.Warning("Incomplete TERMINAL-TYPE request");
-    return 
-  }
-  
-  fb    := buffer[0]
-  if fb != TELNET_TTYPE_IS && fb != TELNET_TTYPE_SEND {
-    monolog.Warning("TERMINAL-TYPE request has invalid type %d (%v)", fb, buffer)
-    return
-  }
-  
-  term := string(buffer[1:])
-  me.SendEvent(&TTypeEvent{fb, term})
-}
+func (me *Telnet) SubnegotiateTType(buffer []byte) {
+	// make sure request is not empty
+	if len(buffer) == 0 {
+		monolog.Warning("Incomplete TERMINAL-TYPE request")
+		return
+	}
+
+	fb := buffer[0]
+	if fb != TELNET_TTYPE_IS && fb != TELNET_TTYPE_SEND {
+		monolog.Warning("TERMINAL-TYPE request has invalid type %d (%v)", fb, buffer)
+		return
+	}
 
+	term := string(buffer[1:])
+	me.SendEvent(&TTypeEvent{fb, term})
+}
 
 // process a subnegotiation buffer; returns true if the current buffer
 // must be aborted and reprocessed due to COMPRESS2 being activated
-func (me * Telnet) DoSubnegotiate(buffer []byte) bool {
-    switch me.sb_telopt {
-        case TELNET_TELOPT_COMPRESS2:
-        // received COMPRESS2 begin marker, setup our zlib box and
-        // start handling the compressed stream if it's not already.
-        me.compress = true
-        me.SendEvent(&CompressEvent{me.compress})
-        return true
-        // specially handled subnegotiation telopt types
-        case TELNET_TELOPT_TTYPE:
-            me.SubnegotiateTType(buffer)
-        case TELNET_TELOPT_ENVIRON:
-            me.SubnegotiateEnviron(buffer)
-        case TELNET_TELOPT_NEW_ENVIRON:
-            me.SubnegotiateEnviron(buffer)
-        case TELNET_TELOPT_MSSP:
-            me.SubnegotiateMSSP(buffer)
-        case TELNET_TELOPT_NAWS:
-            me.SubnegotiateNAWS(buffer)
-        case TELNET_TELOPT_ZMP:
-            me.SubnegotiateZMP(buffer)
-        default:    
-            // Send catch all subnegotiation event
-            me.SendEvent(&SubnegotiateEvent{me.sb_telopt, buffer})
-    }
-    return false
-}
-
-func (me * Telnet) DoNegotiate(state TelnetState, telopt byte) bool {
-    switch me.state {
-        case will_state:
-            me.SendEvent(&WillEvent{telopt})
-        case wont_state:
-            me.SendEvent(&WontEvent{telopt})
-        case do_state:
-            me.SendEvent(&DoEvent{telopt})
-        case dont_state:
-            me.SendEvent(&DontEvent{telopt})
-        default:
-            monolog.Warning("State not vvalid in  telnet negotiation.")
-    }
-    me.state = data_state
-    return false
+func (me *Telnet) DoSubnegotiate(buffer []byte) bool {
+	switch me.sb_telopt {
+	case TELNET_TELOPT_COMPRESS2:
+		// received COMPRESS2 begin marker, setup our zlib box and
+		// start handling the compressed stream if it's not already.
+		me.compress = true
+		me.SendEvent(&CompressEvent{me.compress})
+		return true
+	// specially handled subnegotiation telopt types
+	case TELNET_TELOPT_TTYPE:
+		me.SubnegotiateTType(buffer)
+	case TELNET_TELOPT_ENVIRON:
+		me.SubnegotiateEnviron(buffer)
+	case TELNET_TELOPT_NEW_ENVIRON:
+		me.SubnegotiateEnviron(buffer)
+	case TELNET_TELOPT_MSSP:
+		me.SubnegotiateMSSP(buffer)
+	case TELNET_TELOPT_NAWS:
+		me.SubnegotiateNAWS(buffer)
+	case TELNET_TELOPT_ZMP:
+		me.SubnegotiateZMP(buffer)
+	default:
+		// Send catch all subnegotiation event
+		me.SendEvent(&SubnegotiateEvent{me.sb_telopt, buffer})
+	}
+	return false
+}
+
+func (me *Telnet) DoNegotiate(state TelnetState, telopt byte) bool {
+	switch me.state {
+	case will_state:
+		me.SendEvent(&WillEvent{telopt})
+	case wont_state:
+		me.SendEvent(&WontEvent{telopt})
+	case do_state:
+		me.SendEvent(&DoEvent{telopt})
+	case dont_state:
+		me.SendEvent(&DontEvent{telopt})
+	default:
+		monolog.Warning("State not vvalid in  telnet negotiation.")
+	}
+	me.state = data_state
+	return false
 }
 
 // Send the current buffer as a DataEvent if it's not empty
 // Also empties the buffer if it wasn't emmpty
-func (me * Telnet) maybeSendDataEventAndEmptyBuffer() {
-    if (me.buffer != nil) && (len(me.buffer) > 0) {
-        me.SendEvent(&DataEvent{me.buffer})
-        me.buffer = nil
-    }
+func (me *Telnet) maybeSendDataEventAndEmptyBuffer() {
+	if (me.buffer != nil) && (len(me.buffer) > 0) {
+		me.SendEvent(&DataEvent{me.buffer})
+		me.buffer = nil
+	}
 }
 
 // Append a byte to the data buffer
-func (me * Telnet) appendByte(bin byte) {
-    monolog.Log("TELNET", "Appending to telnet buffer: %d %d", len(me.buffer), cap(me.buffer))
-    me.buffer = append(me.buffer, bin)
-}
-
-// Process a byte in the data state 
-func (me * Telnet) dataStateProcessByte(bin byte) bool {
-    if bin == TELNET_IAC {
-        // receive buffered bytes as data and go to IAC state if it's notempty
-        me.maybeSendDataEventAndEmptyBuffer()
-        me.state = iac_state
-    } else {
-        me.appendByte(bin)
-    }
-    return false
-}
-
-// Process a byte in the IAC state 
-func (me * Telnet) iacStateProcessByte(bin byte) bool {
-    switch bin {
-      // subnegotiation
-      case TELNET_SB:
-        me.state = sb_state
-      // negotiation commands
-      case TELNET_WILL:
-        me.state = will_state
-      case TELNET_WONT:
-        me.state = wont_state
-      case TELNET_DO:
-        me.state = do_state
-      case TELNET_DONT:
-        me.state = dont_state
-      // IAC escaping
-      case TELNET_IAC:
-        me.appendByte(TELNET_IAC)
-        me.maybeSendDataEventAndEmptyBuffer()
-        me.state = data_state
-      // some other command
-      default:
-        me.SendEvent(IACEvent { bin })
-        me.state = data_state
-    }
-    return false      
-}
-
-
-// Process a byte in the subnegotiation data state 
-func (me * Telnet) sbdataStateProcessByte(bin byte) bool {
-    // IAC command in subnegotiation -- either IAC SE or IAC IAC
-    if (bin == TELNET_IAC)  {
-        me.state = sb_data_iac_state
-    } else if me.sb_telopt == TELNET_TELOPT_COMPRESS &&  bin == TELNET_WILL {
-        // MCCPv1 defined an invalid subnegotiation sequence (IAC SB 85 WILL SE) 
-        // to start compression. Catch and discard this case, only support 
-        // MMCPv2.
-        me.state = data_state
-    } else {
-        me.appendByte(bin)
-    }
-    return false
-}
-
-// Process a byte in the IAC received when processing subnegotiation data state 
-func (me * Telnet) sbdataiacStateProcessByte(bin byte) bool {
-    switch bin { 
-        //end subnegotiation
-        case TELNET_SE:
-        me.state = data_state
-        // process subnegotiation
-        compress := me.DoSubnegotiate(me.buffer)
-        // if compression was negotiated, the rest of the stream is compressed
-        // and processing it requires decompressing it. Return true to signal 
-        // this.
-        me.buffer = nil
-        if compress {
-            return true 
-        }
-            
-        // escaped IAC byte
-        case TELNET_IAC:
-        // push IAC into buffer
-        me.appendByte(bin)
-        me.state = sb_data_state
-        // something else -- protocol error.  attempt to process
-        // content in subnegotiation buffer, then evaluate the
-        // given command as an IAC code.
-        default:
-        monolog.Warning("Unexpected byte after IAC inside SB: %d", bin)
-        me.state = iac_state
-        // subnegotiate with the buffer anyway, even though it's an error
-        compress := me.DoSubnegotiate(me.buffer)
-        // if compression was negotiated, the rest of the stream is compressed
-        // and processing it requires decompressing it. Return true to signal 
-        // this.
-        me.buffer = nil
-        if compress {
-            return true 
-        }
-    }
-    return false    
-}
-
-
-// Process a single byte received from the client 
-func (me * Telnet) ProcessByte(bin byte) bool {
-    monolog.Log("TELNET", "ProcessByte %d %d", bin, me.state)
-    switch me.state {
-    // regular data
-        case data_state:
-        return me.dataStateProcessByte(bin)
-    // IAC received before
-        case iac_state:
-        return me.iacStateProcessByte(bin)
-        case will_state, wont_state, do_state, dont_state:
-        return me.DoNegotiate(me.state, bin)
-        // subnegotiation started, determine option to subnegotiate
-        case sb_state:
-        me.sb_telopt = bin      
-        me.state     = sb_data_state
-        // subnegotiation data, buffer bytes until the end request 
-        case sb_data_state:
-        return me.sbdataStateProcessByte(bin)
-        // IAC received inside a subnegotiation
-        case sb_data_iac_state:
-        return me.sbdataiacStateProcessByte(bin)
-        default:
-            //  programing error, shouldn't happen
-            panic("Error in telnet state machine!")        
-    }
-    // return false to signal compression needn't start
-    return false
-}
- 
+func (me *Telnet) appendByte(bin byte) {
+	monolog.Log("TELNET", "Appending to telnet buffer: %d %d", len(me.buffer), cap(me.buffer))
+	me.buffer = append(me.buffer, bin)
+}
+
+// Process a byte in the data state
+func (me *Telnet) dataStateProcessByte(bin byte) bool {
+	if bin == TELNET_IAC {
+		// receive buffered bytes as data and go to IAC state if it's notempty
+		me.maybeSendDataEventAndEmptyBuffer()
+		me.state = iac_state
+	} else {
+		me.appendByte(bin)
+	}
+	return false
+}
+
+// Process a byte in the IAC state
+func (me *Telnet) iacStateProcessByte(bin byte) bool {
+	switch bin {
+	// subnegotiation
+	case TELNET_SB:
+		me.state = sb_state
+	// negotiation commands
+	case TELNET_WILL:
+		me.state = will_state
+	case TELNET_WONT:
+		me.state = wont_state
+	case TELNET_DO:
+		me.state = do_state
+	case TELNET_DONT:
+		me.state = dont_state
+	// IAC escaping
+	case TELNET_IAC:
+		me.appendByte(TELNET_IAC)
+		me.maybeSendDataEventAndEmptyBuffer()
+		me.state = data_state
+	// some other command
+	default:
+		me.SendEvent(IACEvent{bin})
+		me.state = data_state
+	}
+	return false
+}
+
+// Process a byte in the subnegotiation data state
+func (me *Telnet) sbdataStateProcessByte(bin byte) bool {
+	// IAC command in subnegotiation -- either IAC SE or IAC IAC
+	if bin == TELNET_IAC {
+		me.state = sb_data_iac_state
+	} else if me.sb_telopt == TELNET_TELOPT_COMPRESS && bin == TELNET_WILL {
+		// MCCPv1 defined an invalid subnegotiation sequence (IAC SB 85 WILL SE)
+		// to start compression. Catch and discard this case, only support
+		// MMCPv2.
+		me.state = data_state
+	} else {
+		me.appendByte(bin)
+	}
+	return false
+}
+
+// Process a byte in the IAC received when processing subnegotiation data state
+func (me *Telnet) sbdataiacStateProcessByte(bin byte) bool {
+	switch bin {
+	//end subnegotiation
+	case TELNET_SE:
+		me.state = data_state
+		// process subnegotiation
+		compress := me.DoSubnegotiate(me.buffer)
+		// if compression was negotiated, the rest of the stream is compressed
+		// and processing it requires decompressing it. Return true to signal
+		// this.
+		me.buffer = nil
+		if compress {
+			return true
+		}
+
+	// escaped IAC byte
+	case TELNET_IAC:
+		// push IAC into buffer
+		me.appendByte(bin)
+		me.state = sb_data_state
+	// something else -- protocol error.  attempt to process
+	// content in subnegotiation buffer, then evaluate the
+	// given command as an IAC code.
+	default:
+		monolog.Warning("Unexpected byte after IAC inside SB: %d", bin)
+		me.state = iac_state
+		// subnegotiate with the buffer anyway, even though it's an error
+		compress := me.DoSubnegotiate(me.buffer)
+		// if compression was negotiated, the rest of the stream is compressed
+		// and processing it requires decompressing it. Return true to signal
+		// this.
+		me.buffer = nil
+		if compress {
+			return true
+		}
+	}
+	return false
+}
+
+// Process a single byte received from the client
+func (me *Telnet) ProcessByte(bin byte) bool {
+	monolog.Log("TELNET", "ProcessByte %d %d", bin, me.state)
+	switch me.state {
+	// regular data
+	case data_state:
+		return me.dataStateProcessByte(bin)
+		// IAC received before
+	case iac_state:
+		return me.iacStateProcessByte(bin)
+	case will_state, wont_state, do_state, dont_state:
+		return me.DoNegotiate(me.state, bin)
+	// subnegotiation started, determine option to subnegotiate
+	case sb_state:
+		me.sb_telopt = bin
+		me.state = sb_data_state
+	// subnegotiation data, buffer bytes until the end request
+	case sb_data_state:
+		return me.sbdataStateProcessByte(bin)
+	// IAC received inside a subnegotiation
+	case sb_data_iac_state:
+		return me.sbdataiacStateProcessByte(bin)
+	default:
+		//  programing error, shouldn't happen
+		panic("Error in telnet state machine!")
+	}
+	// return false to signal compression needn't start
+	return false
+}
+
 // Process multiple bytes received from the client
-func (me * Telnet) ProcessBytes(bytes []byte) {
-    for index := 0 ; index < len(bytes) ; {
-        bin := bytes[index]
-        compress := me.ProcessByte(bin)
-        if compress {
-            // paper over this for a while... 
-            // new_bytes = Zlib.inflate(arr.pack('c*')) rescue nil
-            // if new_bytes
-            //arr = new_bytes.bytes.to_a
-        }
-        index ++
-    }
-    me.maybeSendDataEventAndEmptyBuffer()
-}
-
-  
+func (me *Telnet) ProcessBytes(bytes []byte) {
+	for index := 0; index < len(bytes); {
+		bin := bytes[index]
+		compress := me.ProcessByte(bin)
+		if compress {
+			// paper over this for a while...
+			// new_bytes = Zlib.inflate(arr.pack('c*')) rescue nil
+			// if new_bytes
+			//arr = new_bytes.bytes.to_a
+		}
+		index++
+	}
+	me.maybeSendDataEventAndEmptyBuffer()
+}
+
 // Call this when the server receives data from the client
-func (me * Telnet) TelnetReceive(data []byte) {
-// the COMPRESS2 protocol seems to be half-duplex in that only 
-// the server's data stream is compressed (unless maybe if the client
-// is asked to also compress with a DO command ?)
-    me.ProcessBytes(data)
+func (me *Telnet) TelnetReceive(data []byte) {
+	// the COMPRESS2 protocol seems to be half-duplex in that only
+	// the server's data stream is compressed (unless maybe if the client
+	// is asked to also compress with a DO command ?)
+	me.ProcessBytes(data)
 }
 
 // Send a bytes array (raw) to the client
-func (me * Telnet) TelnetSendBytes(bytes ...byte) {
-    me.SendRaw(bytes)
+func (me *Telnet) TelnetSendBytes(bytes ...byte) {
+	me.SendRaw(bytes)
 }
 
-// Send an iac command 
-func (me * Telnet) TelnetSendIac(cmd byte) {
-    me.TelnetSendBytes(TELNET_IAC, cmd)
+// Send an iac command
+func (me *Telnet) TelnetSendIac(cmd byte) {
+	me.TelnetSendBytes(TELNET_IAC, cmd)
 }
 
-// Send negotiation. Currently rfc1143 is not implemented, so beware of 
-// server client loops. The simplest way to avoid those is to never answer any 
+// Send negotiation. Currently rfc1143 is not implemented, so beware of
+// server client loops. The simplest way to avoid those is to never answer any
 // client requests, only send server requests.
-func (me * Telnet) TelnetSendNegotiate(cmd byte, telopt byte) {
-    me.TelnetSendBytes(TELNET_IAC, cmd, telopt)
+func (me *Telnet) TelnetSendNegotiate(cmd byte, telopt byte) {
+	me.TelnetSendBytes(TELNET_IAC, cmd, telopt)
 }
-        
+
 // Send non-command data (escapes IAC bytes)
-func (me * Telnet) TelnetSend(buffer []byte) {
-    me.SendEscaped(buffer)
+func (me *Telnet) TelnetSend(buffer []byte) {
+	me.SendEscaped(buffer)
 }
-  
+
 // send subnegotiation header
-func (me * Telnet) TelnetBeginSubnegotiation(telopt byte) {
-    me.TelnetSendBytes(TELNET_IAC, TELNET_SB, telopt)
+func (me *Telnet) TelnetBeginSubnegotiation(telopt byte) {
+	me.TelnetSendBytes(TELNET_IAC, TELNET_SB, telopt)
 }
 
-
 // send subnegotiation ending
-func (me * Telnet) TelnetEndSubnegotiation() {
-    me.TelnetSendBytes(TELNET_IAC, TELNET_SE)
+func (me *Telnet) TelnetEndSubnegotiation() {
+	me.TelnetSendBytes(TELNET_IAC, TELNET_SE)
 }
 
 // Send complete subnegotiation
-func (me * Telnet) TelnetSubnegotiation(telopt byte, buffer []byte) {
-    me.TelnetBeginSubnegotiation(telopt)
-    if buffer != nil {
-        me.TelnetSend(buffer) 
-    }
-    me.TelnetEndSubnegotiation()
-}
-  
+func (me *Telnet) TelnetSubnegotiation(telopt byte, buffer []byte) {
+	me.TelnetBeginSubnegotiation(telopt)
+	if buffer != nil {
+		me.TelnetSend(buffer)
+	}
+	me.TelnetEndSubnegotiation()
+}
+
 // Ask client to start accepting compress2 compression
-func (me * Telnet) TelnetBeginCompress2() {
-    me.TelnetSendBytes(TELNET_IAC, TELNET_SB, TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE);
-    me.compress = true
+func (me *Telnet) TelnetBeginCompress2() {
+	me.TelnetSendBytes(TELNET_IAC, TELNET_SB, TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE)
+	me.compress = true
 }
 
 // Send formatted data to the client
-func (me * Telnet) TelnetRawPrintf(format string, args ...interface{}) {
-    buf  := fmt.Sprintf(format, args...)
-    me.TelnetSend([]byte(buf))
+func (me *Telnet) TelnetRawPrintf(format string, args ...interface{}) {
+	buf := fmt.Sprintf(format, args...)
+	me.TelnetSend([]byte(buf))
 }
 
-const CRLF  = "\r\n"
+const CRLF = "\r\n"
 const CRNUL = "\r\000"
-  
-// send formatted data with \r and \n translation in addition to IAC IAC 
+
+// send formatted data with \r and \n translation in addition to IAC IAC
 // escaping
-func (me * Telnet) TelnetPrintf(format string, args ...interface{}) {
-    buf  := fmt.Sprintf(format, args...)
-    buf   = strings.Replace(buf, "\r", CRNUL, -1)
-    buf   = strings.Replace(buf, "\n", CRLF , -1)
-    me.TelnetSend([]byte(buf))
+func (me *Telnet) TelnetPrintf(format string, args ...interface{}) {
+	buf := fmt.Sprintf(format, args...)
+	buf = strings.Replace(buf, "\r", CRNUL, -1)
+	buf = strings.Replace(buf, "\n", CRLF, -1)
+	me.TelnetSend([]byte(buf))
 }
 
 // NEW-ENVIRON subnegotation
-func (me * Telnet) TelnetNewenviron(cmd []byte) {
-    me.TelnetSubnegotiation(TELNET_TELOPT_NEW_ENVIRON, cmd)
+func (me *Telnet) TelnetNewenviron(cmd []byte) {
+	me.TelnetSubnegotiation(TELNET_TELOPT_NEW_ENVIRON, cmd)
 }
 
 // send TERMINAL-TYPE SEND command
-func (me * Telnet)  TelnetTTypeSend() {
-    me.TelnetSendBytes(TELNET_IAC, TELNET_SB, TELNET_TELOPT_TTYPE, TELNET_TTYPE_SEND, TELNET_IAC, TELNET_SE)
+func (me *Telnet) TelnetTTypeSend() {
+	me.TelnetSendBytes(TELNET_IAC, TELNET_SB, TELNET_TELOPT_TTYPE, TELNET_TTYPE_SEND, TELNET_IAC, TELNET_SE)
 }
 
-// send TERMINAL-TYPE IS command 
-func (me * Telnet)  TelnetTTypeIS(ttype string) {
-    me.TelnetSendBytes(TELNET_IAC, TELNET_SB, TELNET_TELOPT_TTYPE, TELNET_TTYPE_IS)
-    me.TelnetSend([]byte(ttype))
+// send TERMINAL-TYPE IS command
+func (me *Telnet) TelnetTTypeIS(ttype string) {
+	me.TelnetSendBytes(TELNET_IAC, TELNET_SB, TELNET_TELOPT_TTYPE, TELNET_TTYPE_IS)
+	me.TelnetSend([]byte(ttype))
 }
 
 // send MSSP data
-func (me * Telnet) TelnetSendMSSP(mssp map[string] string) {
-    var buf []byte 
-    for key, val := range mssp { 
-      buf = append(buf, TELNET_MSSP_VAR)
-      buf = append(buf, key...)
-      buf = append(buf, TELNET_MSSP_VAL)
-      buf = append(buf, val...)
-    }
-    me.TelnetSubnegotiation(TELNET_TELOPT_MSSP, buf)
+func (me *Telnet) TelnetSendMSSP(mssp map[string]string) {
+	var buf []byte
+	for key, val := range mssp {
+		buf = append(buf, TELNET_MSSP_VAR)
+		buf = append(buf, key...)
+		buf = append(buf, TELNET_MSSP_VAL)
+		buf = append(buf, val...)
+	}
+	me.TelnetSubnegotiation(TELNET_TELOPT_MSSP, buf)
 }
-
-
-