 
                
        
     
                
        
     
                
        
     
                
        
     
                
        
     
                
        
     
                
        
     
                
        
     
                
        
     
                
        
     
                
        
     
                
        
     
                
        
    on data:
for each byte in data:
if byte == IAC:
buffer cmd byte
expect(COMMAND || SUBNEGOTIATION) //2 more bytes: WILL/WONT/DO/DONT option, OR subnegotiation
else if byte == SB:
buffer SB byte
expect(SB_OPTION)
 
                
        
     
                
        
     
                
        
    on data:
for each byte in data:
if byte == IAC:
buffer cmd byte
expect(COMMAND || SUBNEGOTIATION) //2 more bytes: WILL/WONT/DO/DONT option, OR subnegotiation
else if byte == SB:
buffer SB byte
expect(SB_OPTION)
function TelnetParser() {
  this.state = "text";
}
function TelnetParser.prototype.parse(data) {
  // Convert from binary Buffer to UTF-8-encoded string
  // It's just easier for the sake of example than using a Buffer directly.
  var a = [];
  for (var i = 0, len = data.length; i < len; ++i) {
    a[i] = String.fromCharCode(data[i]);
  }
  data = a.join("");
  
  var nextNonText, ch;
  for (var i = 0, len = data.length; i < len; ++i) {
    ch = data[i];
    
    switch (this.state) {
      case "text":
        nextNonText = Math.min(data.indexOf("\r", i), data.indexOf("\xFF", i));
        if (nextNonText === -1) {
          // emit everything up to the end as a text event
          i = len; // break out early
        } else {
          // emit everything up to this point as a text event
          
          i = nextNonText;
          this.state == (data[nextNonText] == "\r") ? "cr" : "iac";
        }
        break;
      
      case "cr":
        if (ch == "\n") {
          // emit "\r\n"
        } else if (ch == "\0") {
          // emit "\r"
        } else {
          // emit "\r", and don't consume this char.
          –i;
        }
        this.state = "text";
        break;
      
      case "iac":
        // more goes here
        break;
    }
  }
} 
                
        
    
IAC DO LINEMODE
IAC SB 0 1 IAC SE
\r \n
The 1 above is for the EDIT mode. All of the above is sent in a single stream. This does indeed disable linemode, but I don't think it's correct because I get a bunch of weird crap back from the client. For enabling, I am sending the reverse:
IAC DO LINEMODE
IAC SB 1 1 IAC SE
\r \n
That does not re-enable linemode.
I thought I was understanding the RFC properly, but obviously I'm not. What is the correct way to switch between character mode and linemode?