Handle byte type on sockets
[mudpy.git] / lib / mudpy / misc.py
index 1bef3f3..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.
 
@@ -142,7 +142,7 @@ class Element:
             return default
 
     def getint(self, facet, default=None):
-        """Return values as int/long type."""
+        """Return values as int type."""
         if default is None:
             default = 0
         if self.origin.data.has_option(self.key, facet):
@@ -192,18 +192,17 @@ class Element:
     def set(self, facet, value):
         """Set values."""
         if not self.has_facet(facet) or not self.get(facet) == value:
-            if type(value) is long or repr(type(value)) == "<type 'unicode'>":
+            # TODO: remove this check after the switch to py3k
+            if repr(type(value)) == "<type 'unicode'>":
                 value = str(value)
-            elif not type(value) is str:
+            if not type(value) is str:
                 value = repr(value)
             self.origin.data.set(self.key, facet, value)
             self.origin.modified = True
 
     def append(self, facet, value):
-        """Append value tp a list."""
-        if type(value) is long:
-            value = str(value)
-        elif not type(value) is str:
+        """Append value to a list."""
+        if not type(value) is str:
             value = repr(value)
         newlist = self.getlist(facet)
         newlist.append(value)
@@ -350,7 +349,7 @@ class Element:
             description = element.get("description")
             if description:
                 message += description + "$(eol)"
-            portal_list = element.portals().keys()
+            portal_list = list(element.portals().keys())
             if portal_list:
                 portal_list.sort()
                 message += "$(cyn)[ Exits: " + ", ".join(
@@ -590,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 = {}
@@ -925,7 +924,7 @@ class User:
         try:
             raw_input = self.connection.recv(1024)
         except:
-            raw_input = ""
+            raw_input = b""
 
         # we got something
         if raw_input:
@@ -937,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:
@@ -959,7 +958,8 @@ class User:
                 # log non-printable characters remaining
                 if mudpy.telnet.is_enabled(self, mudpy.telnet.TELOPT_BINARY,
                                            mudpy.telnet.HIM):
-                    asciiline = 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."""
@@ -1652,7 +1664,7 @@ def get_menu_choices(user):
 def get_formatted_menu_choices(state, choices):
     """Returns a formatted string of menu choices."""
     choice_output = ""
-    choice_keys = choices.keys()
+    choice_keys = list(choices.keys())
     choice_keys.sort()
     for choice in choice_keys:
         choice_output += "   [$(red)" + choice + "$(nrm)]  " + choices[
@@ -1773,9 +1785,9 @@ def handler_entering_account_name(user):
         name = input_data.lower()
 
         # fail if there are non-alphanumeric characters
-        if name != filter(
-           lambda x: x >= "0" and x <= "9" or x >= "a" and x <= "z", name
-           ):
+        if name != "".join(filter(
+                lambda x: x >= "0" and x <= "9" or x >= "a" and x <= "z",
+                name)):
             user.error = "bad_name"
 
         # if that account exists, time to request a password
@@ -1839,11 +1851,11 @@ def handler_entering_new_password(user):
     # make sure the password is strong--at least one upper, one lower and
     # one digit, seven or more characters in length
     if len(input_data) > 6 and len(
-       filter(lambda x: x >= "0" and x <= "9", input_data)
+       list(filter(lambda x: x >= "0" and x <= "9", input_data))
        ) and len(
-        filter(lambda x: x >= "A" and x <= "Z", input_data)
+        list(filter(lambda x: x >= "A" and x <= "Z", input_data))
     ) and len(
-        filter(lambda x: x >= "a" and x <= "z", input_data)
+        list(filter(lambda x: x >= "a" and x <= "z", input_data))
     ):
 
         # hash and store it, then move on to verification
@@ -2050,7 +2062,7 @@ def command_help(actor, parameters):
 
         # give a sorted list of commands with descriptions if provided
         output = "These are the commands available to you:$(eol)$(eol)"
-        sorted_commands = universe.categories["command"].keys()
+        sorted_commands = list(universe.categories["command"].keys())
         sorted_commands.sort()
         for item in sorted_commands:
             command = universe.categories["command"][item]
@@ -2190,13 +2202,13 @@ def command_show(actor, parameters):
         ) + " increments elapsed since the world was created."
     elif arguments[0] == "categories":
         message = "These are the element categories:$(eol)"
-        categories = universe.categories.keys()
+        categories = list(universe.categories.keys())
         categories.sort()
         for category in categories:
             message += "$(eol)   $(grn)" + category + "$(nrm)"
     elif arguments[0] == "files":
         message = "These are the current files containing the universe:$(eol)"
-        filenames = universe.files.keys()
+        filenames = list(universe.files.keys())
         filenames.sort()
         for filename in filenames:
             if universe.files[filename].is_writeable():