Handle byte type on sockets
authorJeremy Stanley <fungi@yuggoth.org>
Tue, 19 Mar 2013 03:10:40 +0000 (03:10 +0000)
committerJeremy Stanley <fungi@yuggoth.org>
Tue, 19 Mar 2013 03:10:40 +0000 (03:10 +0000)
* bin/test
* lib/mudpy/misc.py
* lib/mudpy/telnet.py: For Py3K compatibility, socket I/O uses byte type
instead of str.

bin/test
lib/mudpy/misc.py
lib/mudpy/telnet.py

index 9dec9f4..480c7be 100755 (executable)
--- a/bin/test
+++ b/bin/test
@@ -2,32 +2,32 @@
 # -*- coding: utf-8 -*-
 """Regression test script for the mudpy engine."""
 
-# Copyright (c) 2004-2011 Jeremy Stanley <fungi@yuggoth.org>. Permission
+# Copyright (c) 2004-2013 Jeremy Stanley <fungi@yuggoth.org>. Permission
 # to use, copy, modify, and distribute this software is granted under
 # terms provided in the LICENSE file distributed with this software.
 
 conversation = (
-    ("Identify yourself:", "testuser"),
-    ("Enter your choice:", "n"),
-    ("Enter a new password for \"testuser\":", "Test123"),
-    ("Enter the same new password again:", "Test123"),
-    ("What would you like to do?", "c"),
-    ("Pick a gender for your new avatar:", "f"),
-    ("Choose a name for her:", "1"),
-    ("What would you like to do?", "a"),
-    ("Whom would you like to awaken?", ""),
-    (">", "quit"),
-    ("What would you like to do?", "d"),
-    ("Whom would you like to delete?", ""),
-    ("What would you like to do?", "p"),
-    ("permanently delete your account?", "y"),
-    ("Disconnecting...", ""),
+    (b"Identify yourself:", b"testuser"),
+    (b"Enter your choice:", b"n"),
+    (b"Enter a new password for \"testuser\":", b"Test123"),
+    (b"Enter the same new password again:", b"Test123"),
+    (b"What would you like to do?", b"c"),
+    (b"Pick a gender for your new avatar:", b"f"),
+    (b"Choose a name for her:", b"1"),
+    (b"What would you like to do?", b"a"),
+    (b"Whom would you like to awaken?", b""),
+    (b">", b"quit"),
+    (b"What would you like to do?", b"d"),
+    (b"Whom would you like to delete?", b""),
+    (b"What would you like to do?", b"p"),
+    (b"permanently delete your account?", b"y"),
+    (b"Disconnecting...", b""),
 )
 
 import telnetlib
 mud = telnetlib.Telnet()
 mud.open("::1", 6669)
 for question, answer in conversation:
-    mud.read_until("%s " % question)
-    mud.write("%s\r\n" % answer)
+    mud.read_until(b"%s " % question)
+    mud.write(b"%s\r\n" % answer)
 mud.close()
index 3193e11..8f6630f 100644 (file)
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 """Miscellaneous functions for the mudpy engine."""
 
-# Copyright (c) 2004-2012 Jeremy Stanley <fungi@yuggoth.org>. Permission
+# Copyright (c) 2004-2013 Jeremy Stanley <fungi@yuggoth.org>. Permission
 # to use, copy, modify, and distribute this software is granted under
 # terms provided in the LICENSE file distributed with this software.
 
@@ -589,7 +589,7 @@ class User:
         self.menu_seen = False
         self.negotiation_pause = 0
         self.output_queue = []
-        self.partial_input = ""
+        self.partial_input = b""
         self.password_tries = 0
         self.state = "initial"
         self.telopts = {}
@@ -924,7 +924,7 @@ class User:
         try:
             raw_input = self.connection.recv(1024)
         except:
-            raw_input = ""
+            raw_input = b""
 
         # we got something
         if raw_input:
@@ -936,18 +936,18 @@ class User:
             mudpy.telnet.negotiate_telnet_options(self)
 
             # separate multiple input lines
-            new_input_lines = self.partial_input.split("\n")
+            new_input_lines = self.partial_input.split(b"\n")
 
             # if input doesn't end in a newline, replace the
             # held partial input with the last line of it
-            if not self.partial_input.endswith("\n"):
+            if not self.partial_input.endswith(b"\n"):
                 self.partial_input = new_input_lines.pop()
 
             # otherwise, chop off the extra null input and reset
             # the held partial input
             else:
                 new_input_lines.pop()
-                self.partial_input = ""
+                self.partial_input = b""
 
             # iterate over the remaining lines
             for line in new_input_lines:
@@ -958,8 +958,8 @@ class User:
                 # log non-printable characters remaining
                 if mudpy.telnet.is_enabled(self, mudpy.telnet.TELOPT_BINARY,
                                            mudpy.telnet.HIM):
-                    asciiline = "".join(
-                        filter(lambda x: " " <= x <= "~", line))
+                    asciiline = b"".join(
+                        filter(lambda x: b" " <= x <= b"~", line))
                     if line != asciiline:
                         logline = "Non-ASCII characters from "
                         if self.account and self.account.get("name"):
@@ -970,10 +970,22 @@ class User:
                         log(logline, 4)
                         line = asciiline
 
+                try:
+                    line = line.decode("utf-8")
+                except UnicodeDecodeError:
+                    logline = "Non-UTF-8 characters from "
+                    if self.account and self.account.get("name"):
+                        logline += self.account.get("name") + ": "
+                    else:
+                        logline += "unknown user: "
+                    logline += repr(line)
+                    log(logline, 4)
+                    return
+
+                line = unicodedata.normalize("NFKC", line)
+
                 # put on the end of the queue
-                self.input_queue.append(
-                    unicodedata.normalize("NFKC", line.decode("utf-8"))
-                )
+                self.input_queue.append(line)
 
     def new_avatar(self):
         """Instantiate a new, unconfigured avatar for this user."""
index 3ce73a3..7765571 100644 (file)
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 """Telnet functions and constants for the mudpy engine."""
 
-# Copyright (c) 2004-2012 Jeremy Stanley <fungi@yuggoth.org>. Permission
+# Copyright (c) 2004-2013 Jeremy Stanley <fungi@yuggoth.org>. Permission
 # to use, copy, modify, and distribute this software is granted under
 # terms provided in the LICENSE file distributed with this software.
 
@@ -49,8 +49,7 @@ US = 1
 
 def telnet_proto(*arguments):
     """Return a concatenated series of Telnet protocol commands."""
-    # (this will need to be byte type during 2to3 migration)
-    return "".join([chr(x) for x in arguments])
+    return bytes((arguments))
 
 
 def send_command(user, *command):
@@ -126,8 +125,7 @@ def negotiate_telnet_options(user):
             break
 
         # the byte following the IAC is our command
-        # (this will need to be byte type during 2to3 migration)
-        command = ord(text[position + 1])
+        command = text[position+1]
 
         # replace a double (literal) IAC if there's an LF later
         if command is IAC:
@@ -139,8 +137,7 @@ def negotiate_telnet_options(user):
 
         # implement an RFC 1143 option negotiation queue here
         elif len_text > position + 2 and WILL <= command <= DONT:
-            # this will need to be byte type during 2to3 migration
-            telopt = ord(text[position + 2])
+            telopt = text[position+2]
             if telopt in supported:
                 if command <= WONT:
                     party = HIM
@@ -184,10 +181,8 @@ def negotiate_telnet_options(user):
 
         # subnegotiation options
         elif len_text > position + 4 and command is SB:
-            # this will need to be byte type during 2to3 migration
             telopt = ord(text[position + 2])
             if telopt is TELOPT_NAWS:
-                # this will need to be byte type during 2to3 migration
                 user.columns = ord(text[position + 3]) * \
                     256 + ord(text[position + 4])
             end_subnegotiation = text.find(telnet_proto(IAC, SE), position)