Start checking codebase with the codespell tool
[mudpy.git] / mudpy / telnet.py
index 0ce9aec..ec18f70 100644 (file)
@@ -1,6 +1,6 @@
 """Telnet functions and constants for the mudpy engine."""
 
-# Copyright (c) 2004-2018 mudpy authors. Permission to use, copy,
+# Copyright (c) 2004-2020 mudpy authors. Permission to use, copy,
 # modify, and distribute this software is granted under terms
 # provided in the LICENSE file distributed with this software.
 
@@ -24,11 +24,11 @@ option_names = {
     TELOPT_LINEMODE: "line mode",
 }
 
-# TODO(fungi): implement support for RFC 1091 terminal type information
 supported = (
     TELOPT_BINARY,
     TELOPT_ECHO,
     TELOPT_SGA,
+    TELOPT_TTYPE,
     TELOPT_EOR,
     TELOPT_NAWS,
     TELOPT_LINEMODE
@@ -80,6 +80,14 @@ party_names = {
     US: "us",
 }
 
+# RFC 1091 commands
+IS = 0
+SEND = 1
+ttype_command_names = {
+    IS: "is",
+    SEND: "send",
+}
+
 
 def log(message, user):
     """Log debugging info for Telnet client/server interactions."""
@@ -97,7 +105,20 @@ def telnet_proto(*arguments):
 
 def translate_action(*command):
     """Convert a Telnet command sequence into text suitable for logging."""
-    return "%s %s" % (command_names[command[0]], option_names[command[1]])
+    try:
+        command_name = command_names[command[0]]
+    except KeyError:
+        # This should never happen since we filter unknown commands from
+        # the input queue, but added here for completeness since logging
+        # should never crash the process
+        command_name = str(command[0])
+    try:
+        option_name = option_names[command[1]]
+    except KeyError:
+        # This can happen for any of the myriad of Telnet options missing
+        # from the option_names dict
+        option_name = str(command[1])
+    return "%s %s" % (command_name, option_name)
 
 
 def send_command(user, *command):
@@ -150,13 +171,24 @@ def disable(user, telopt, party):
         user.telopts[(telopt, party)] = WANTNO
 
 
+def request_ttype(user):
+    """Clear and request the terminal type."""
+
+    # only actually request if the corresponding telopt is enabled
+    if is_enabled(user, TELOPT_TTYPE, HIM):
+        # set to the empty string to indicate it's been requested
+        user.ttype = ""
+        user.send(telnet_proto(IAC, SB, TELOPT_TTYPE, SEND, IAC, SE), raw=True)
+        log('Sent terminal type request to', user)
+
+
 def negotiate_telnet_options(user):
     """Reply to and remove telnet negotiation options from partial_input."""
 
     # make a local copy to play with
     text = user.partial_input
 
-    # start at the begining of the input
+    # start at the beginning of the input
     position = 0
 
     # as long as we haven't checked it all
@@ -232,11 +264,16 @@ def negotiate_telnet_options(user):
         # subnegotiation options
         elif len_text > position + 4 and command is SB:
             telopt = text[position + 2]
-            if telopt is TELOPT_NAWS:
-                user.columns = (
-                    text[position + 3] * 256 + text[position + 4])
             end_subnegotiation = text.find(telnet_proto(IAC, SE), position)
             if end_subnegotiation > 0:
+                if telopt is TELOPT_NAWS:
+                    user.columns = (
+                        text[position + 3] * 256 + text[position + 4])
+                    user.rows = (
+                        text[position + 5] * 256 + text[position + 6])
+                elif telopt is TELOPT_TTYPE and text[position + 3] is IS:
+                    user.ttype = (
+                        text[position + 4:end_subnegotiation]).decode("ascii")
                 text = text[:position] + text[end_subnegotiation + 2:]
             else:
                 position += 1
@@ -246,7 +283,7 @@ def negotiate_telnet_options(user):
             log("Ignored unknown command %s from" % command, user)
             text = text[:position] + text[position + 2:]
 
-        # and this means we got the begining of an IAC
+        # and this means we got the beginning of an IAC
         else:
             position += 1