diff --git a/evennia/server/sessionhandler.py b/evennia/server/sessionhandler.py index 734e6e0c27..89d6afde89 100644 --- a/evennia/server/sessionhandler.py +++ b/evennia/server/sessionhandler.py @@ -24,11 +24,9 @@ from evennia.utils.utils import (variable_from_module, is_iter, make_iter, callables_from_module) from evennia.utils.inlinefuncs import parse_inlinefunc +from codecs import decode as codecs_decode -try: - import pickle as pickle -except ImportError: - import pickle +import pickle _INLINEFUNC_ENABLED = settings.INLINEFUNC_ENABLED @@ -39,6 +37,8 @@ _ServerConfig = None _ScriptDB = None _OOB_HANDLER = None +_ERR_BAD_UTF8 = 'Your client sent an incorrect UTF-8 sequence.' + class DummySession(object): sessid = 0 @@ -47,17 +47,8 @@ class DummySession(object): DUMMYSESSION = DummySession() # AMP signals -PCONN = chr(1) # portal session connect -PDISCONN = chr(2) # portal session disconnect -PSYNC = chr(3) # portal session sync -SLOGIN = chr(4) # server session login -SDISCONN = chr(5) # server session disconnect -SDISCONNALL = chr(6) # server session disconnect all -SSHUTD = chr(7) # server shutdown -SSYNC = chr(8) # server session sync -SCONN = chr(11) # server portal connection (for bots) -PCONNSYNC = chr(12) # portal post-syncing session -PDISCONNALL = chr(13) # portal session discnnect all +from .amp import (PCONN, PDISCONN, PSYNC, SLOGIN, SDISCONN, SDISCONNALL, + SSHUTD, SSYNC, SCONN, PCONNSYNC, PDISCONNALL, ) # i18n from django.utils.translation import ugettext as _ @@ -185,6 +176,21 @@ class SessionHandler(dict): raw = options.get("raw", False) strip_inlinefunc = options.get("strip_inlinefunc", False) + def _utf8(data): + if isinstance(data, bytes): + try: + data = codecs_decode(data, session.protocol_flags["ENCODING"]) + except LookupError: + # wrong encoding set on the session. Set it to a safe one + session.protocol_flags["ENCODING"] = "utf-8" + data = codecs_decode(data, "utf-8") + except UnicodeDecodeError: + # incorrect unicode sequence + session.sendLine(_ERR_BAD_UTF8) + data = '' + + return data + def _validate(data): "Helper function to convert data to AMP-safe (picketable) values" if isinstance(data, dict): @@ -192,24 +198,15 @@ class SessionHandler(dict): for key, part in list(data.items()): newdict[key] = _validate(part) return newdict - elif hasattr(data, "__iter__"): + elif is_iter(data): return [_validate(part) for part in data] - elif isinstance(data, str): - # make sure strings are in a valid encoding - try: - data = data and to_str(to_unicode(data), encoding=session.protocol_flags["ENCODING"]) - except LookupError: - # wrong encoding set on the session. Set it to a safe one - session.protocol_flags["ENCODING"] = "utf-8" - data = to_str(to_unicode(data), encoding=session.protocol_flags["ENCODING"]) + elif isinstance(data, (str, bytes, )): + data = _utf8(data) + if _INLINEFUNC_ENABLED and not raw and isinstance(self, ServerSessionHandler): # only parse inlinefuncs on the outgoing path (sessionhandler->) data = parse_inlinefunc(data, strip=strip_inlinefunc, session=session) - # At this point the object is certainly the right encoding, but may still be a unicode object-- - # to_str does not actually force objects to become bytestrings. - # If the unicode object is a subclass of unicode, such as ANSIString, this can cause a problem, - # as special behavior for that class will still be in play. Since we're now transferring raw data, - # we must now force this to be a proper bytestring. + return str(data) elif hasattr(data, "id") and hasattr(data, "db_date_created") \ and hasattr(data, '__dbclass__'): @@ -229,10 +226,10 @@ class SessionHandler(dict): rkwargs[key] = [[], {}] elif isinstance(data, dict): rkwargs[key] = [[], _validate(data)] - elif hasattr(data, "__iter__"): + elif is_iter(data): if isinstance(data[-1], dict): if len(data) == 2: - if hasattr(data[0], "__iter__"): + if is_iter(data[0]): rkwargs[key] = [_validate(data[0]), _validate(data[1])] else: rkwargs[key] = [[_validate(data[0])], _validate(data[1])]