Example #1
0
 def _validate(data):
     "Helper function to convert data to AMP-safe (picketable) values"
     if isinstance(data, dict):
         newdict = {}
         for key, part in data.items():
             newdict[key] = _validate(part)
         return newdict
     elif hasattr(data, "__iter__"):
         return [_validate(part) for part in data]
     elif isinstance(data, basestring):
         # 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"])
         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__'):
         # convert database-object to their string representation.
         return _validate(unicode(data))
     else:
         return data
Example #2
0
    def all(self, category=None, return_key_and_category=False):
        """
        Get all tags in this handler.

        Args:
            category (str, optional): The Tag category to limit the
                request to. Note that `None` is the valid, default
                category.
            return_key_and_category (bool, optional): Return a list of
                tuples `[(key, category), ...]`.

        Returns:
            tags (list): A list of tag keys `[tagkey, tagkey, ...]` or
                a list of tuples `[(key, category), ...]` if
                `return_key_and_category` is set.

        """
        if self._cache is None or not _TYPECLASS_AGGRESSIVE_CACHE:
            self._recache()
        if category:
            category = category.strip().lower() if category is not None else None
            matches = [tag for tag in self._cache.values() if tag.db_category == category]
        else:
            matches = self._cache.values()

        if matches:
            matches = sorted(matches, key=lambda o: o.id)
            if return_key_and_category:
                # return tuple (key, category)
                return [(to_str(p.db_key), to_str(p.db_category)) for p in matches]
            else:
                return [to_str(p.db_key) for p in matches]
        return []
Example #3
0
 def validate_encoding(val):
     # helper: change encoding
     try:
         to_str(to_unicode("test-string"), encoding=val)
     except LookupError:
         raise RuntimeError("The encoding '|w%s|n' is invalid. " % val)
     return val
Example #4
0
    def all(self, category=None, return_key_and_category=False):
        """
        Get all tags in this handler.

        Args:
            category (str, optional): The Tag category to limit the
                request to. Note that `None` is the valid, default
                category.
            return_key_and_category (bool, optional): Return a list of
                tuples `[(key, category), ...]`.

        Returns:
            tags (list): A list of tag keys `[tagkey, tagkey, ...]` or
                a list of tuples `[(key, category), ...]` if
                `return_key_and_category` is set.

        """
        if not self._cache_complete:
            self._fullcache()
        tags = sorted(self._getcache(None, category), key=lambda o:o.id)
        if return_key_and_category:
                # return tuple (key, category)
            return [(to_str(tag.db_key), to_str(tag.db_category)) for tag in tags]
        else:
            return [to_str(tag.db_key) for tag in tags]
        return []
Example #5
0
    def all(self, return_key_and_category=False, return_objs=False):
        """
        Get all tags in this handler, regardless of category.

        Args:
            return_key_and_category (bool, optional): Return a list of
                tuples `[(key, category), ...]`.
            return_objs (bool, optional): Return tag objects.

        Returns:
            tags (list): A list of tag keys `[tagkey, tagkey, ...]` or
                a list of tuples `[(key, category), ...]` if
                `return_key_and_category` is set.

        """
        if not self._cache_complete:
            self._fullcache()
        tags = sorted(self._cache.values())
        if return_key_and_category:
                # return tuple (key, category)
            return [(to_str(tag.db_key), to_str(tag.db_category)) for tag in tags]
        elif return_objs:
            return tags
        else:
            return [to_str(tag.db_key) for tag in tags]
Example #6
0
 def validate_encoding(new_encoding):
     # helper: change encoding
     try:
         utils.to_str(utils.to_unicode("test-string"), encoding=new_encoding)
     except LookupError:
         raise RuntimeError("The encoding '|w%s|n' is invalid. " % new_encoding)
     return val
Example #7
0
    def reload(self, filename=None, form=None, **kwargs):
        """
        Creates the form from a stored file name
        """
        # clean kwargs (these cannot be overridden)
        kwargs.pop("enforce_size", None)
        kwargs.pop("width", None)
        kwargs.pop("height", None)

        if form or self.input_form_dict:
            datadict = form if form else self.input_form_dict
            self.input_form_dict = datadict
        elif filename or self.filename:
            filename = filename if filename else self.filename
            datadict = all_from_module(filename)
            self.filename = filename
        else:
            datadict = {}

        cellchar = to_str(datadict.get("FORMCHAR", "x"))
        self.cellchar = to_str(cellchar[0] if len(cellchar) > 1 else cellchar)
        tablechar = datadict.get("TABLECHAR", "c")
        self.tablechar = tablechar[0] if len(tablechar) > 1 else tablechar

        # split into a list of list of lines. Form can be indexed with form[iy][ix]
        self.raw_form = _to_ansi(to_unicode(datadict.get("FORM", "")).split("\n"))
        # strip first line
        self.raw_form = self.raw_form[1:] if self.raw_form else self.raw_form

        self.options.update(kwargs)

        # parse and replace
        self.mapping = self._parse_rectangles(self.cellchar, self.tablechar, self.raw_form, **kwargs)
        self.form = self._populate_form(self.raw_form, self.mapping)
Example #8
0
 def _validate(data):
     "Helper function to convert data to AMP-safe (picketable) values"
     if isinstance(data, dict):
         newdict = {}
         for key, part in data.items():
             newdict[key] = _validate(part)
         return newdict
     elif hasattr(data, "__iter__"):
         return [_validate(part) for part in data]
     elif isinstance(data, basestring):
         # 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"])
         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)
         return data
     elif hasattr(data, "id") and hasattr(data, "db_date_created") \
             and hasattr(data, '__dbclass__'):
         # convert database-object to their string representation.
         return _validate(unicode(data))
     else:
         return data
Example #9
0
    def __new__(cls, *args, **kwargs):
        """
        When creating a new ANSIString, you may use a custom parser that has
        the same attributes as the standard one, and you may declare the
        string to be handled as already decoded. It is important not to double
        decode strings, as escapes can only be respected once.

        Internally, ANSIString can also passes itself precached code/character
        indexes and clean strings to avoid doing extra work when combining
        ANSIStrings.

        """
        string = args[0]
        if not isinstance(string, basestring):
            string = to_str(string, force_string=True)
        parser = kwargs.get('parser', ANSI_PARSER)
        decoded = kwargs.get('decoded', False) or hasattr(string, '_raw_string')
        code_indexes = kwargs.pop('code_indexes', None)
        char_indexes = kwargs.pop('char_indexes', None)
        clean_string = kwargs.pop('clean_string', None)
        # All True, or All False, not just one.
        checks = [x is None for x in [code_indexes, char_indexes, clean_string]]
        if not len(set(checks)) == 1:
            raise ValueError("You must specify code_indexes, char_indexes, "
                             "and clean_string together, or not at all.")
        if not all(checks):
            decoded = True
        if not decoded:
            # Completely new ANSI String
            clean_string = to_unicode(parser.parse_ansi(string, strip_ansi=True, mxp=True))
            string = parser.parse_ansi(string, xterm256=True, mxp=True)
        elif clean_string is not None:
            # We have an explicit clean string.
            pass
        elif hasattr(string, '_clean_string'):
            # It's already an ANSIString
            clean_string = string._clean_string
            code_indexes = string._code_indexes
            char_indexes = string._char_indexes
            string = string._raw_string
        else:
            # It's a string that has been pre-ansi decoded.
            clean_string = parser.strip_raw_codes(string)

        if not isinstance(string, unicode):
            string = string.decode('utf-8')

        ansi_string = super(ANSIString, cls).__new__(ANSIString, to_str(clean_string), "utf-8")
        ansi_string._raw_string = string
        ansi_string._clean_string = clean_string
        ansi_string._code_indexes = code_indexes
        ansi_string._char_indexes = char_indexes
        return ansi_string
Example #10
0
    def get(self, key, default=None, category=None, return_tagobj=False):
        """
        Get the tag for the given key or list of tags.

        Args:
            key (str or list): The tag or tags to retrieve.
            default (any, optional): The value to return in case of no match.
            category (str, optional): The Tag category to limit the
                request to. Note that `None` is the valid, default
                category.
            return_tagobj (bool, optional): Return the Tag object itself
                instead of a string representation of the Tag.

        Returns:
            tags (str, TagObject or list): The matches, either string
                representations of the tags or the Tag objects themselves
                depending on `return_tagobj`.

        """
        if self._cache is None or not _TYPECLASS_AGGRESSIVE_CACHE:
            self._recache()
        ret = []
        category = category.strip().lower() if category is not None else None
        searchkey = ["%s-%s" % (key.strip().lower(), category) if key is not None else None for key in make_iter(key)]
        ret = [val for val in (self._cache.get(keystr) for keystr in searchkey) if val]
        ret = [to_str(tag.db_data) for tag in ret] if return_tagobj else ret
        return ret[0] if len(ret) == 1 else (ret if ret else default)
Example #11
0
    def data_out(self, text=None, **kwargs):
        """
        Data Evennia -> User. A generic hook method for engine to call
        in order to send data through the websocket connection.

        Kwargs:
            oob (str or tuple): Supply an Out-of-Band instruction.
            raw (bool): No parsing at all (leave ansi-to-html markers unparsed).
            nomarkup (bool): Clean out all ansi/html markers and tokens.

        """
        try:
            text = to_str(text if text else "", encoding=self.encoding)
        except Exception as e:
            self.sendLine(str(e))
        if "oob" in kwargs:
            for cmdname, args, okwargs in kwargs["oob"]:
                self.json_encode(cmdname, *args, **okwargs)

        raw = kwargs.get("raw", False)
        nomarkup = kwargs.get("nomarkup", False)
        if "prompt" in kwargs:
            self.sendLine("PRT" + parse_html(kwargs["prompt"], strip_ansi=nomarkup))
        if raw:
            self.sendLine("CMD" + text)
        else:
            self.sendLine("CMD" + parse_html(text, strip_ansi=nomarkup))
Example #12
0
    def get(self, key=None, default=None, category=None, return_tagobj=False):
        """
        Get the tag for the given key or list of tags.

        Args:
            key (str or list): The tag or tags to retrieve.
            default (any, optional): The value to return in case of no match.
            category (str, optional): The Tag category to limit the
                request to. Note that `None` is the valid, default
                category.
            return_tagobj (bool, optional): Return the Tag object itself
                instead of a string representation of the Tag.

        Returns:
            tags (str, TagObject or list): The matches, either string
                representations of the tags or the Tag objects themselves
                depending on `return_tagobj`.

        """
        ret = []
        for keystr in make_iter(key):
            # note - the _getcache call removes case sensitivity for us
            ret.extend([tag if return_tagobj else to_str(tag.db_key)
                            for tag in self._getcache(keystr, category)])
        return ret[0] if len(ret) == 1 else (ret if ret else default)
Example #13
0
 def _set_foreign(cls, fname, value):
     "Setter only used on foreign key relations, allows setting with #dbref"
     if _GA(cls, "_is_deleted"):
         raise ObjectDoesNotExist("Cannot set %s to %s: Hosting object was already deleted!" % (fname, value))
     try:
         value = _GA(value, "dbobj")
     except AttributeError:
         pass
     if isinstance(value, (basestring, int)):
         value = to_str(value, force_string=True)
         if (value.isdigit() or value.startswith("#")):
             # we also allow setting using dbrefs, if so we try to load the matching object.
             # (we assume the object is of the same type as the class holding the field, if
             # not a custom handler must be used for that field)
             dbid = dbref(value, reqhash=False)
             if dbid:
                 model = _GA(cls, "_meta").get_field(fname).model
                 try:
                     value = model._default_manager.get(id=dbid)
                 except ObjectDoesNotExist:
                     # maybe it is just a name that happens to look like a dbid
                     pass
     _SA(cls, fname, value)
     # only use explicit update_fields in save if we actually have a
     # primary key assigned already (won't be set when first creating object)
     update_fields = [fname] if _GA(cls, "_get_pk_val")(_GA(cls, "_meta")) is not None else None
     _GA(cls, "save")(update_fields=update_fields)
Example #14
0
    def __set_obj(self, value):
        """
        Set account or obj to their right database field. If
        a dbref is given, assume ObjectDB.

        """
        try:
            value = _GA(value, "dbobj")
        except AttributeError:
            # deprecated ...
            pass
        if isinstance(value, (basestring, int)):
            from evennia.objects.models import ObjectDB
            value = to_str(value, force_string=True)
            if (value.isdigit() or value.startswith("#")):
                dbid = dbref(value, reqhash=False)
                if dbid:
                    try:
                        value = ObjectDB.objects.get(id=dbid)
                    except ObjectDoesNotExist:
                        # maybe it is just a name that happens to look like a dbid
                        pass
        if value.__class__.__name__ == "AccountDB":
            fname = "db_account"
            _SA(self, fname, value)
        else:
            fname = "db_obj"
            _SA(self, fname, value)
        # saving the field
        _GA(self, "save")(update_fields=[fname])
Example #15
0
    def msg(self, text=None, from_obj=None, session=None, **kwargs):
        """
        Evennia -> User
        This is the main route for sending data back to the user from the
        server.

        Args:
            text (str, optional): text data to send
            from_obj (Object or Player, optional): Object sending. If given,
                its at_msg_send() hook will be called.
            session (Session or list, optional): Session object or a list of
                Sessions to receive this send. If given, overrules the
                default send behavior for the current
                MULTISESSION_MODE.
        Notes:
            All other keywords are passed on to the protocol.

        """
        text = to_str(text, force_string=True) if text else ""

        if from_obj:
            # call hook
            try:
                from_obj.at_msg_send(text=text, to_obj=self, **kwargs)
            except Exception:
                pass

        # session relay
        sessions = make_iter(session) if session else self.sessions.all()
        for session in sessions:
            session.msg(text=text, **kwargs)
Example #16
0
 def dataReceived(self, data):
     """
     Handle non-AMP messages, such as HTTP communication.
     """
     if data[0] == NUL:
         # an AMP communication
         if data[-2:] != NULNUL:
             # an incomplete AMP box means more batches are forthcoming.
             self.multibatches += 1
         try:
             super(AMPMultiConnectionProtocol, self).dataReceived(data)
         except KeyError:
             _get_logger().log_trace("Discarded incoming partial data: {}".format(to_str(data)))
     elif self.multibatches:
         # invalid AMP, but we have a pending multi-batch that is not yet complete
         if data[-2:] == NULNUL:
             # end of existing multibatch
             self.multibatches = max(0, self.multibatches - 1)
         try:
             super(AMPMultiConnectionProtocol, self).dataReceived(data)
         except KeyError:
             _get_logger().log_trace("Discarded incoming multi-batch data:".format(to_str(data)))
     else:
         # not an AMP communication, return warning
         self.transport.write(_HTTP_WARNING)
         self.transport.loseConnection()
         print("HTML received: %s" % data)
Example #17
0
    def get(self, key=None, default=None, category=None, return_tagobj=False, return_list=False):
        """
        Get the tag for the given key, category or combination of the two.

        Args:
            key (str or list, optional): The tag or tags to retrieve.
            default (any, optional): The value to return in case of no match.
            category (str, optional): The Tag category to limit the
                request to. Note that `None` is the valid, default
                category. If no `key` is given, all tags of this category will be
                returned.
            return_tagobj (bool, optional): Return the Tag object itself
                instead of a string representation of the Tag.
            return_list (bool, optional): Always return a list, regardless
                of number of matches.

        Returns:
            tags (list): The matches, either string
                representations of the tags or the Tag objects themselves
                depending on `return_tagobj`. If 'default' is set, this
                will be a list with the default value as its only element.

        """
        ret = []
        for keystr in make_iter(key):
            # note - the _getcache call removes case sensitivity for us
            ret.extend([tag if return_tagobj else to_str(tag.db_key)
                        for tag in self._getcache(keystr, category)])
        if return_list:
            return ret if ret else [default] if default is not None else []
        return ret[0] if len(ret) == 1 else (ret if ret else default)
Example #18
0
    def data_out(self, text=None, **kwargs):
        """
        Data Evennia -> Player.
        generic hook method for engine to call in order to send data
        through the telnet connection.

        valid telnet kwargs:
            oob=[(cmdname,args,kwargs), ...] - supply an Out-of-Band instruction.
            xterm256=True/False - enforce xterm256 setting. If not
                                  given, ttype result is used. If
                                  client does not suport xterm256, the
                                  ansi fallback will be used
            mxp=True/False - enforce mxp setting. If not given, enables if we
                             detected client support for it
            ansi=True/False - enforce ansi setting. If not given,
                              ttype result is used.
            nomarkup=True - strip all ansi markup (this is the same as
                            xterm256=False, ansi=False)
            raw=True - pass string through without any ansi
                       processing (i.e. include Evennia ansi markers but do
                       not convert them into ansi tokens)
            prompt=<string> - supply a prompt text which gets sent without a
                              newline added to the end
            echo=True/False
        The telnet ttype negotiation flags, if any, are used if no kwargs
        are given.
        """
        try:
            text = utils.to_str(text if text else "", encoding=self.encoding)
        except Exception, e:
            self.sendLine(str(e))
            return
Example #19
0
 def _recache(self):
     "Cache all attributes of this object"
     query = {"%s__id" % self._model : self._objid,
              "attribute__db_attrtype" : self._attrtype}
     attrs = [conn.attribute for conn in getattr(self.obj, self._m2m_fieldname).through.objects.filter(**query)]
     self._cache = dict(("%s-%s" % (to_str(attr.db_key).lower(),
                                    attr.db_category.lower() if attr.db_category else None),
                         attr) for attr in attrs)
Example #20
0
    def func(self):
        """
        Sets the encoding.
        """

        if self.session is None:
            return

        sync = False
        if 'clear' in self.switches:
            # remove customization
            old_encoding = self.session.protocol_flags.get("ENCODING", None)
            if old_encoding:
                string = "Your custom text encoding ('%s') was cleared." % old_encoding
            else:
                string = "No custom encoding was set."
            self.session.protocol_flags["ENCODING"] = "utf-8"
            sync = True
        elif not self.args:
            # just list the encodings supported
            pencoding = self.session.protocol_flags.get("ENCODING", None)
            string = ""
            if pencoding:
                string += "Default encoding: |g%s|n (change with |w@encoding <encoding>|n)" % pencoding
            encodings = settings.ENCODINGS
            if encodings:
                string += "\nServer's alternative encodings (tested in this order):\n   |g%s|n" % ", ".join(encodings)
            if not string:
                string = "No encodings found."
        else:
            # change encoding
            old_encoding = self.session.protocol_flags.get("ENCODING", None)
            encoding = self.args
            try:
                utils.to_str(utils.to_unicode("test-string"), encoding=encoding)
            except LookupError:
                string = "|rThe encoding '|w%s|r' is invalid. Keeping the previous encoding '|w%s|r'.|n"\
                         % (encoding, old_encoding)
            else:
                self.session.protocol_flags["ENCODING"] = encoding
                string = "Your custom text encoding was changed from '|w%s|n' to '|w%s|n'." % (old_encoding, encoding)
                sync = True
        if sync:
            self.session.sessionhandler.session_portal_sync(self.session)
        self.caller.msg(string.strip())
Example #21
0
def oob_return_field_report(session, fieldname, obj, *args, **kwargs):
    """
    This is a helper command called by the monitor when fieldname
    changes. It is not part of the official MSDP specification but is
    a callback used by the monitor to format the result before sending
    it on.
    """
    session.msg(oob=("MSDP_TABLE", (),
                     {fieldname: to_str(getattr(obj, fieldname), force_string=True)}))
Example #22
0
    def call(self, cmdobj, args, msg=None, cmdset=None, noansi=True, caller=None, receiver=None, cmdstring=None, obj=None):
        """
        Test a command by assigning all the needed
        properties to cmdobj and  running
            cmdobj.at_pre_cmd()
            cmdobj.parse()
            cmdobj.func()
            cmdobj.at_post_cmd()
        The msgreturn value is compared to eventual
        output sent to caller.msg in the game

        Returns:
            msg (str): The received message that was sent to the caller.

        """
        caller = caller if caller else self.char1
        receiver = receiver if receiver else caller
        cmdobj.caller = caller
        cmdobj.cmdstring = cmdstring if cmdstring else cmdobj.key
        cmdobj.args = args
        cmdobj.cmdset = cmdset
        cmdobj.session = SESSIONS.session_from_sessid(1)
        cmdobj.player = self.player
        cmdobj.raw_string = cmdobj.key + " " + args
        cmdobj.obj = obj or (caller if caller else self.char1)
        # test
        old_msg = receiver.msg
        returned_msg = ""
        try:
            receiver.msg = Mock()
            cmdobj.at_pre_cmd()
            cmdobj.parse()
            cmdobj.func()
            cmdobj.at_post_cmd()
        except InterruptCommand:
            pass
        finally:
            # clean out prettytable sugar. We only operate on text-type
            stored_msg = [args[0] if args and args[0] else kwargs.get("text",utils.to_str(kwargs, force_string=True))
                    for name, args, kwargs in receiver.msg.mock_calls]
            # Get the first element of a tuple if msg received a tuple instead of a string
            stored_msg = [smsg[0] if isinstance(smsg, tuple) else smsg for smsg in stored_msg]
            if msg is not None:
                returned_msg = "||".join(_RE.sub("", mess) for mess in stored_msg)
                returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip()
                if msg == "" and returned_msg or not returned_msg.startswith(msg.strip()):
                    sep1 = "\n" + "="*30 + "Wanted message" + "="*34 + "\n"
                    sep2 = "\n" + "="*30 + "Returned message" + "="*32 + "\n"
                    sep3 = "\n" + "="*78
                    retval = sep1 + msg.strip() + sep2 + returned_msg + sep3
                    raise AssertionError(retval)
            else:
                returned_msg = "\n".join(str(msg) for msg in stored_msg)
                returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip()
            receiver.msg = old_msg

        return returned_msg
Example #23
0
 def _fullcache(self):
     "Cache all tags of this object"
     query = {"%s__id" % self._model: self._objid, "tag__db_tagtype": self._tagtype}
     tags = [conn.tag for conn in getattr(self.obj, self._m2m_fieldname).through.objects.filter(**query)]
     self._cache = dict(
         ("%s-%s" % (to_str(tag.db_key).lower(), tag.db_category.lower() if tag.db_category else None), tag)
         for tag in tags
     )
     self._cache_complete = True
Example #24
0
def protfunc_parser(value, available_functions=None, testing=False, stacktrace=False, **kwargs):
    """
    Parse a prototype value string for a protfunc and process it.

    Available protfuncs are specified as callables in one of the modules of
    `settings.PROTFUNC_MODULES`, or specified on the command line.

    Args:
        value (any): The value to test for a parseable protfunc. Only strings will be parsed for
            protfuncs, all other types are returned as-is.
        available_functions (dict, optional): Mapping of name:protfunction to use for this parsing.
            If not set, use default sources.
        testing (bool, optional): Passed to protfunc. If in a testing mode, some protfuncs may
            behave differently.
        stacktrace (bool, optional): If set, print the stack parsing process of the protfunc-parser.

    Kwargs:
        session (Session): Passed to protfunc. Session of the entity spawning the prototype.
        protototype (dict): Passed to protfunc. The dict this protfunc is a part of.
        current_key(str): Passed to protfunc. The key in the prototype that will hold this value.
        any (any): Passed on to the protfunc.

    Returns:
        testresult (tuple): If `testing` is set, returns a tuple (error, result) where error is
            either None or a string detailing the error from protfunc_parser or seen when trying to
            run `literal_eval` on the parsed string.
        any (any): A structure to replace the string on the prototype level. If this is a
            callable or a (callable, (args,)) structure, it will be executed as if one had supplied
            it to the prototype directly. This structure is also passed through literal_eval so one
            can get actual Python primitives out of it (not just strings). It will also identify
            eventual object #dbrefs in the output from the protfunc.

    """
    if not isinstance(value, basestring):
        try:
            value = value.dbref
        except AttributeError:
            pass
        value = to_str(value, force_string=True)

    available_functions = PROT_FUNCS if available_functions is None else available_functions

    result = inlinefuncs.parse_inlinefunc(
        value, available_funcs=available_functions,
        stacktrace=stacktrace, testing=testing, **kwargs)

    err = None
    try:
        result = literal_eval(result)
    except ValueError:
        pass
    except Exception as err:
        err = str(err)
    if testing:
        return err, result
    return result
Example #25
0
 def __init__(self, obj):
     "Initialize handler."
     self.obj = obj
     self._objid = obj.id
     self._model = to_str(obj.__dbclass__.__name__.lower())
     self._cache = {}
     # store category names fully cached
     self._catcache = {}
     # full cache was run on all attributes
     self._cache_complete = False
Example #26
0
 def look_target(self):
     "Hook method for when an argument is given."
     player = self.player
     key = self.args.lower()
     chars = dict((utils.to_str(char.key.lower()), char) for char in player.db._playable_characters)
     looktarget = chars.get(key)
     if looktarget:
         self.msg(looktarget.return_appearance(player))
     else:
         self.msg("No such character.")
     return
Example #27
0
    def parse_ansi(self, string, strip_ansi=False, xterm256=False, mxp=False):
        """
        Parses a string, subbing color codes according to
        the stored mapping.

        strip_ansi flag instead removes all ANSI markup.

        """
        if hasattr(string, '_raw_string'):
            if strip_ansi:
                return string.clean()
            else:
                return string.raw()

        if not string:
            return ''

        # check cached parsings
        global _PARSE_CACHE
        cachekey = "%s-%s-%s-%s" % (string, strip_ansi, xterm256, mxp)
        if cachekey in _PARSE_CACHE:
            return _PARSE_CACHE[cachekey]

        # pre-convert bright colors to xterm256 color tags
        string = self.brightbg_sub.sub(self.sub_brightbg, string)

        def do_xterm256(part):
            return self.sub_xterm256(part, xterm256)

        in_string = utils.to_str(string)

        # do string replacement
        parsed_string =  ""
        parts = self.ansi_escapes.split(in_string) + [" "]
        for part, sep in zip(parts[::2], parts[1::2]):
            pstring = self.xterm256_sub.sub(do_xterm256, part)
            pstring = self.ansi_sub.sub(self.sub_ansi, pstring)
            parsed_string += "%s%s" % (pstring, sep[0].strip())

        if not mxp:
            parsed_string = self.strip_mxp(parsed_string)

        if strip_ansi:
            # remove all ansi codes (including those manually
            # inserted in string)
            return self.strip_raw_codes(parsed_string)

        # cache and crop old cache
        _PARSE_CACHE[cachekey] = parsed_string
        if len(_PARSE_CACHE) > _PARSE_CACHE_SIZE:
           _PARSE_CACHE.popitem(last=False)

        return parsed_string
Example #28
0
    def _send_packet(self, packet):
        """
        Helper function to send packets across the wire.

        Args:
            packet (Packet): Outgoing packet.

        """
        packet.imc2_protocol = self
        packet_str = utils.to_str(packet.assemble(self.factory.mudname,
                         self.factory.client_pwd, self.factory.server_pwd))
        self.sendLine(packet_str)
Example #29
0
    def send_text(self, *args, **kwargs):
        """
        Send text data. This is an in-band telnet operation.

        Args:
            text (str): The first argument is always the text string to send. No other arguments
                are considered.
        Kwargs:
            options (dict): Send-option flags
                   - mxp: Enforce MXP link support.
                   - ansi: Enforce no ANSI colors.
                   - xterm256: Enforce xterm256 colors, regardless of TTYPE setting.
                   - nocolor: Strip all colors.
                   - raw: Pass string through without any ansi processing
                        (i.e. include Evennia ansi markers but do not
                        convert them into ansi tokens)
                   - echo: Turn on/off line echo on the client. Turn
                        off line echo for client, for example for password.
                        Note that it must be actively turned back on again!

        """
        # print "telnet.send_text", args,kwargs  # DEBUG
        text = args[0] if args else ""
        if text is None:
            return
        text = to_str(text, force_string=True)

        # handle arguments
        options = kwargs.get("options", {})
        flags = self.protocol_flags
        xterm256 = options.get("xterm256", flags.get('XTERM256', True))
        useansi = options.get("ansi", flags.get('ANSI', True))
        raw = options.get("raw", flags.get("RAW", False))
        nocolor = options.get("nocolor", flags.get("NOCOLOR") or not (xterm256 or useansi))
        # echo = options.get("echo", None)  # DEBUG
        screenreader = options.get("screenreader", flags.get("SCREENREADER", False))

        if screenreader:
            # screenreader mode cleans up output
            text = ansi.parse_ansi(text, strip_ansi=True, xterm256=False, mxp=False)
            text = _RE_SCREENREADER_REGEX.sub("", text)

        if raw:
            # no processing
            self.sendLine(text)
            return
        else:
            # we need to make sure to kill the color at the end in order
            # to match the webclient output.
            linetosend = ansi.parse_ansi(_RE_N.sub("", text) + ("||n" if text.endswith("|") else "|n"),
                                         strip_ansi=nocolor, xterm256=xterm256, mxp=False)
            self.sendLine(linetosend)
Example #30
0
    def data_out(self, session, text="", **kwargs):
        """
        Sending data Server -> Portal

        Args:
            session (Session): Session object
            text (str, optional): text data to return
            _nomulti (bool, optional): if given, only this
                session will receive the rest of the data,
                regardless of MULTISESSION_MODE. This is an
                internal variable that will not be passed on.
                This is ignored for MULTISESSION_MODE = 1,
                since all messages are mirrored everywhere for
                that.
            _forced_nomulti (bool, optional): Like _nomulti,
                but works even when MULTISESSION_MODE = 1.
                Useful for connection handling messages.

        """
        #from evennia.server.profiling.timetrace import timetrace
        #text = timetrace(text, "ServerSessionHandler.data_out")

        sessions = make_iter(session)
        session = sessions[0]
        text = text and to_str(to_unicode(text), encoding=session.encoding)
        multi = not kwargs.pop("_nomulti", None)
        forced_nomulti = kwargs.pop("_forced_nomulti", None)
        # Mode 1 mirrors to all.
        if _MULTISESSION_MODE == 1:
            multi = True
        # ...Unless we're absolutely sure.
        if forced_nomulti:
            multi = False

        if multi:
            if _MULTISESSION_MODE == 1:
                if session.player:
                    sessions = self.sessions_from_player(session.player)
            if _MULTISESSION_MODE == 2:
                if session.player:
                    sessions = self.sessions_from_player(session.player)
            elif _MULTISESSION_MODE == 3:
                if session.puppet:
                    sessions = self.sessions_from_puppet(session.puppet)
                elif session.player:
                    sessions = self.sessions_from_player(session.player)

        # send to all found sessions
        for session in sessions:
            self.server.amp_protocol.send_MsgServer2Portal(sessid=session.sessid,
                                                           msg=text,
                                                           data=kwargs)
Example #31
0
    def call(self,
             cmdobj,
             args,
             msg=None,
             cmdset=None,
             noansi=True,
             caller=None,
             receiver=None,
             cmdstring=None,
             obj=None):
        """
        Test a command by assigning all the needed
        properties to cmdobj and  running
            cmdobj.at_pre_cmd()
            cmdobj.parse()
            cmdobj.func()
            cmdobj.at_post_cmd()
        The msgreturn value is compared to eventual
        output sent to caller.msg in the game

        Returns:
            msg (str): The received message that was sent to the caller.

        """
        caller = caller if caller else self.char1
        receiver = receiver if receiver else caller
        cmdobj.caller = caller
        cmdobj.cmdname = cmdstring if cmdstring else cmdobj.key
        cmdobj.raw_cmdname = cmdobj.cmdname
        cmdobj.cmdstring = cmdobj.cmdname  # deprecated
        cmdobj.args = args
        cmdobj.cmdset = cmdset
        cmdobj.session = SESSIONS.session_from_sessid(1)
        cmdobj.account = self.account
        cmdobj.raw_string = cmdobj.key + " " + args
        cmdobj.obj = obj or (caller if caller else self.char1)
        # test
        old_msg = receiver.msg
        returned_msg = ""
        try:
            receiver.msg = Mock()
            cmdobj.at_pre_cmd()
            cmdobj.parse()
            ret = cmdobj.func()
            if isinstance(ret, types.GeneratorType):
                ret.next()
            cmdobj.at_post_cmd()
        except StopIteration:
            pass
        except InterruptCommand:
            pass
        finally:
            # clean out evtable sugar. We only operate on text-type
            stored_msg = [
                args[0] if args and args[0] else kwargs.get(
                    "text", utils.to_str(kwargs, force_string=True))
                for name, args, kwargs in receiver.msg.mock_calls
            ]
            # Get the first element of a tuple if msg received a tuple instead of a string
            stored_msg = [
                smsg[0] if isinstance(smsg, tuple) else smsg
                for smsg in stored_msg
            ]
            if msg is not None:
                returned_msg = "||".join(
                    _RE.sub("", mess) for mess in stored_msg)
                returned_msg = ansi.parse_ansi(returned_msg,
                                               strip_ansi=noansi).strip()
                if msg == "" and returned_msg or not returned_msg.startswith(
                        msg.strip()):
                    sep1 = "\n" + "=" * 30 + "Wanted message" + "=" * 34 + "\n"
                    sep2 = "\n" + "=" * 30 + "Returned message" + "=" * 32 + "\n"
                    sep3 = "\n" + "=" * 78
                    retval = sep1 + msg.strip() + sep2 + returned_msg + sep3
                    raise AssertionError(retval)
            else:
                returned_msg = "\n".join(str(msg) for msg in stored_msg)
                returned_msg = ansi.parse_ansi(returned_msg,
                                               strip_ansi=noansi).strip()
            receiver.msg = old_msg

        return returned_msg
Example #32
0
 def _send_packet(self, packet):
     "Helper function to send packets across the wire"
     packet.imc2_protocol = self
     packet_str = utils.to_str(packet.assemble(self.factory.mudname,
                      self.factory.client_pwd, self.factory.server_pwd))
     self.sendLine(packet_str)
Example #33
0
def run_async(to_execute, *args, **kwargs):
    """
    Runs a function or executes a code snippet asynchronously.

    Inputs:
    to_execute (callable) - if this is a callable, it will
            be executed with *args and non-reserver *kwargs as
            arguments.
            The callable will be executed using ProcPool, or in
            a thread if ProcPool is not available.
    to_execute (string) - this is only available is ProcPool is
            running. If a string, to_execute this will be treated as a code
            snippet to execute asynchronously. *args are then not used
            and non-reserverd *kwargs are used to define the execution
            environment made available to the code.

    reserved kwargs:
        'use_thread' (bool) - this only works with callables (not code).
                     It forces the code to run in a thread instead
                     of using the Process Pool, even if the latter
                     is available. This could be useful if you want
                     to make sure to not get out of sync with the
                     main process (such as accessing in-memory global
                     properties)
        'proc_timeout' (int) - only used if ProcPool is available. Sets a
                     max time for execution. This alters the value set
                     by settings.PROCPOOL_TIMEOUT
        'at_return' -should point to a callable with one argument.
                    It will be called with the return value from
                    to_execute.
        'at_return_kwargs' - this dictionary which be used as keyword
                             arguments to the at_return callback.
        'at_err' - this will be called with a Failure instance if
                       there is an error in to_execute.
        'at_err_kwargs' - this dictionary will be used as keyword
                          arguments to the at_err errback.
        'procpool_name' - the Service name of the procpool to use.
                          Default is PythonProcPool.

    *args   - if to_execute is a callable, these args will be used
              as arguments for that function. If to_execute is a string
              *args are not used.
    *kwargs - if to_execute is a callable, these kwargs will be used
              as keyword arguments in that function. If a string, they
              instead are used to define the executable environment
              that should be available to execute the code in to_execute.

    run_async will either relay the code to a thread or to a processPool
    depending on input and what is available in the system. To activate
    Process pooling, settings.PROCPOOL_ENABLE must be set.

    to_execute in string form should handle all imports needed. kwargs
    can be used to send objects and properties. Such properties will
    be pickled, except Database Objects which will be sent across
    on a special format and re-loaded on the other side.

    To get a return value from your code snippet, Use the _return()
    function: Every call to this function from your snippet will
    append the argument to an internal list of returns. This return value
    (or a list) will be the first argument to the at_return callback.

    Use this function with restrain and only for features/commands
    that you know has no influence on the cause-and-effect order of your
    game (commands given after the async function might be executed before
    it has finished). Accessing the same property from different
    threads/processes can lead to unpredicted behaviour if you are not
    careful (this is called a "race condition").

    Also note that some databases, notably sqlite3, don't support access from
    multiple threads simultaneously, so if you do heavy database access from
    your to_execute under sqlite3 you will probably run very slow or even get
    tracebacks.

    """
    # handle all global imports.
    global _PPOOL, _SESSIONS

    # get the procpool name, if set in kwargs
    procpool_name = kwargs.get("procpool_name", "PythonProcPool")

    if _PPOOL is None:
        # Try to load process Pool
        from evennia.server.sessionhandler import SESSIONS as _SESSIONS
        try:
            _PPOOL = _SESSIONS.server.services.namedServices.get(
                procpool_name).pool
        except AttributeError:
            _PPOOL = False

    use_timeout = kwargs.pop("proc_timeout", _PPOOL.timeout)

    # helper converters for callbacks/errbacks
    def convert_return(f):
        def func(ret, *args, **kwargs):
            rval = ret["response"] and from_pickle(do_unpickle(
                ret["response"]))
            reca = ret["recached"] and from_pickle(do_unpickle(
                ret["recached"]))
            # recache all indicated objects
            [clean_object_caches(obj) for obj in reca]
            if f:
                return f(rval, *args, **kwargs)
            else:
                return rval

        return func

    def convert_err(f):
        def func(err, *args, **kwargs):
            err.trap(Exception)
            err = err.getErrorMessage()
            if use_timeout and err == _PROC_ERR:
                err = "Process took longer than %ss and timed out." % use_timeout
            if f:
                return f(err, *args, **kwargs)
            else:
                err = "Error reported from subprocess: '%s'" % err
                logger.log_errmsg(err)

        return func

    # handle special reserved input kwargs
    use_thread = kwargs.pop("use_thread", False)
    callback = convert_return(kwargs.pop("at_return", None))
    errback = convert_err(kwargs.pop("at_err", None))
    callback_kwargs = kwargs.pop("at_return_kwargs", {})
    errback_kwargs = kwargs.pop("at_err_kwargs", {})

    if _PPOOL and not use_thread:
        # process pool is running
        if isinstance(to_execute, basestring):
            # run source code in process pool
            cmdargs = {"_timeout": use_timeout}
            cmdargs["source"] = to_str(to_execute)
            if kwargs:
                cmdargs["environment"] = do_pickle(to_pickle(kwargs))
            else:
                cmdargs["environment"] = ""
            # defer to process pool
            deferred = _PPOOL.doWork(ExecuteCode, **cmdargs)
        elif callable(to_execute):
            # execute callable in process
            callname = to_execute.__name__
            cmdargs = {"_timeout": use_timeout}
            cmdargs["source"] = "_return(%s(*args,**kwargs))" % callname
            cmdargs["environment"] = do_pickle(
                to_pickle({
                    callname: to_execute,
                    "args": args,
                    "kwargs": kwargs
                }))
            deferred = _PPOOL.doWork(ExecuteCode, **cmdargs)
        else:
            raise RuntimeError(
                "'%s' could not be handled by the process pool" % to_execute)
    elif callable(to_execute):
        # no process pool available, fall back to old deferToThread mechanism.
        deferred = threads.deferToThread(to_execute, *args, **kwargs)
    else:
        # no appropriate input for this server setup
        raise RuntimeError(
            "'%s' could not be handled by run_async - no valid input or no process pool."
            % to_execute)

    # attach callbacks
    if callback:
        deferred.addCallback(callback, **callback_kwargs)
    deferred.addErrback(errback, **errback_kwargs)
Example #34
0
def do_unpickle(data):
    """Retrieve pickle from pickled string"""
    return loads(to_str(data))
Example #35
0
    def call(self,
             cmdobj,
             args,
             msg=None,
             cmdset=None,
             noansi=True,
             caller=None,
             receiver=None,
             cmdstring=None,
             obj=None):
        """
        Test a command by assigning all the needed
        properties to cmdobj and  running
            cmdobj.at_pre_cmd()
            cmdobj.parse()
            cmdobj.func()
            cmdobj.at_post_cmd()
        The msgreturn value is compared to eventual
        output sent to caller.msg in the game

        Returns:
            msg (str): The received message that was sent to the caller.

        """
        caller = caller if caller else self.char1
        receiver = receiver if receiver else caller
        cmdobj.caller = caller
        cmdobj.cmdstring = cmdstring if cmdstring else cmdobj.key
        cmdobj.args = args
        cmdobj.cmdset = cmdset
        cmdobj.session = SESSIONS.session_from_sessid(1)
        cmdobj.account = self.account
        cmdobj.raw_string = cmdobj.key + " " + args
        cmdobj.obj = obj or (caller if caller else self.char1)
        # test
        old_msg = receiver.msg
        try:
            receiver.msg = Mock()
            if cmdobj.at_pre_cmd():
                return
            cmdobj.parse()
            cmdobj.func()
            cmdobj.at_post_cmd()
        except Exception:
            import traceback
            receiver.msg(traceback.format_exc())
        finally:
            # clean out prettytable sugar. We only operate on text-type
            stored_msg = [
                args[0] if args and args[0] else kwargs.get(
                    "text", utils.to_str(kwargs, force_string=True))
                for name, args, kwargs in receiver.msg.mock_calls
            ]
            # Get the first element of a tuple if msg received a tuple instead of a string
            stored_msg = [
                smsg[0] if hasattr(smsg, '__iter__') else smsg
                for smsg in stored_msg
            ]
            if msg is not None:
                returned_msg = self.format_returned_msg(stored_msg, noansi)
                if msg == "" and returned_msg or returned_msg != msg.strip():
                    sep1 = "\n" + "=" * 30 + "Wanted message" + "=" * 34 + "\n"
                    sep2 = "\n" + "=" * 30 + "Returned message" + "=" * 32 + "\n"
                    sep3 = "\n" + "=" * 78
                    # important - use raw strings for wanted/returned messages so we can see whitespace
                    retval = "%s%r%s%r%s" % (sep1, msg.strip(), sep2,
                                             returned_msg, sep3)
                    raise AssertionError(retval)
            else:
                returned_msg = "\n".join(str(msg) for msg in stored_msg)
                returned_msg = ansi.parse_ansi(returned_msg,
                                               strip_ansi=noansi).strip()
            receiver.msg = old_msg
        return returned_msg
Example #36
0
    def call(
        self,
        cmdobj,
        args,
        msg=None,
        cmdset=None,
        noansi=True,
        caller=None,
        receiver=None,
        cmdstring=None,
        obj=None,
        inputs=None,
        raw_string=None,
    ):
        """
        Test a command by assigning all the needed
        properties to cmdobj and  running
            cmdobj.at_pre_cmd()
            cmdobj.parse()
            cmdobj.func()
            cmdobj.at_post_cmd()
        The msgreturn value is compared to eventual
        output sent to caller.msg in the game

        Returns:
            msg (str): The received message that was sent to the caller.

        """
        caller = caller if caller else self.char1
        receiver = receiver if receiver else caller
        cmdobj.caller = caller
        cmdobj.cmdname = cmdstring if cmdstring else cmdobj.key
        cmdobj.raw_cmdname = cmdobj.cmdname
        cmdobj.cmdstring = cmdobj.cmdname  # deprecated
        cmdobj.args = args
        cmdobj.cmdset = cmdset
        cmdobj.session = SESSIONS.session_from_sessid(1)
        cmdobj.account = self.account
        cmdobj.raw_string = raw_string if raw_string is not None else cmdobj.key + " " + args
        cmdobj.obj = obj or (caller if caller else self.char1)
        # test
        old_msg = receiver.msg
        inputs = inputs or []

        try:
            receiver.msg = Mock()
            if cmdobj.at_pre_cmd():
                return
            cmdobj.parse()
            ret = cmdobj.func()

            # handle func's with yield in them (generators)
            if isinstance(ret, types.GeneratorType):
                while True:
                    try:
                        inp = inputs.pop() if inputs else None
                        if inp:
                            try:
                                ret.send(inp)
                            except TypeError:
                                next(ret)
                                ret = ret.send(inp)
                        else:
                            next(ret)
                    except StopIteration:
                        break

            cmdobj.at_post_cmd()
        except StopIteration:
            pass
        except InterruptCommand:
            pass

        # clean out evtable sugar. We only operate on text-type
        stored_msg = [
            args[0] if args and args[0] else kwargs.get(
                "text", utils.to_str(kwargs))
            for name, args, kwargs in receiver.msg.mock_calls
        ]
        # Get the first element of a tuple if msg received a tuple instead of a string
        stored_msg = [
            str(smsg[0]) if isinstance(smsg, tuple) else str(smsg)
            for smsg in stored_msg
        ]
        if msg is not None:
            msg = str(msg)  # to be safe, e.g. `py` command may return ints
            # set our separator for returned messages based on parsing ansi or not
            msg_sep = "|" if noansi else "||"
            # Have to strip ansi for each returned message for the regex to handle it correctly
            returned_msg = msg_sep.join(
                _RE.sub("", ansi.parse_ansi(mess, strip_ansi=noansi))
                for mess in stored_msg).strip()
            if msg == "" and returned_msg or not returned_msg.startswith(
                    msg.strip()):
                sep1 = "\n" + "=" * 30 + "Wanted message" + "=" * 34 + "\n"
                sep2 = "\n" + "=" * 30 + "Returned message" + "=" * 32 + "\n"
                sep3 = "\n" + "=" * 78
                retval = sep1 + msg.strip() + sep2 + returned_msg + sep3
                raise AssertionError(retval)
        else:
            returned_msg = "\n".join(str(msg) for msg in stored_msg)
            returned_msg = ansi.parse_ansi(returned_msg,
                                           strip_ansi=noansi).strip()
        receiver.msg = old_msg

        return returned_msg
Example #37
0
MSDP_VAR = chr(1)  #^A
MSDP_VAL = chr(2)  #^B
MSDP_TABLE_OPEN = chr(3)  #^C
MSDP_TABLE_CLOSE = chr(4)  #^D
MSDP_ARRAY_OPEN = chr(5)  #^E
MSDP_ARRAY_CLOSE = chr(6)  #^F

# GMCP
GMCP = chr(201)

# General Telnet
IAC = chr(255)
SB = chr(250)
SE = chr(240)

force_str = lambda inp: to_str(inp, force_string=True)

# pre-compiled regexes
# returns 2-tuple
msdp_regex_table = re.compile(
    r"%s\s*(\w*?)\s*%s\s*%s(.*?)%s" %
    (MSDP_VAR, MSDP_VAL, MSDP_TABLE_OPEN, MSDP_TABLE_CLOSE))
# returns 2-tuple
msdp_regex_array = re.compile(
    r"%s\s*(\w*?)\s*%s\s*%s(.*?)%s" %
    (MSDP_VAR, MSDP_VAL, MSDP_ARRAY_OPEN, MSDP_ARRAY_CLOSE))
msdp_regex_var = re.compile(r"%s" % MSDP_VAR)
msdp_regex_val = re.compile(r"%s" % MSDP_VAL)

EVENNIA_TO_GMCP = {
    "client_options": "Core.Supports.Get",
Example #38
0
    def data_out(self, text=None, **kwargs):
        """
        Data Evennia -> User. A generic hook method for engine to call
        in order to send data through the telnet connection.

        Kwargs:
            text (str): Text to send.
            oob (list): `[(cmdname,args,kwargs), ...]`, supply an
                Out-of-Band instruction.
            xterm256 (bool): Enforce xterm256 setting. If not given,
                ttype result is used. If client does not suport xterm256,
                the ansi fallback will be used
            mxp (bool): Enforce mxp setting. If not given, enables if
                we detected client support for it
            ansi (bool): Enforce ansi setting. If not given, ttype
                result is used.
            nomarkup (bool): If True, strip all ansi markup (this is
                the same as `xterm256=False, ansi=False`)
            raw (bool):Pass string through without any ansi processing
                (i.e. include Evennia ansi markers but do not convert them
                into ansi tokens)
            prompt (str): Supply a prompt text which gets sent without
                a newline added to the end.
            echo (str): Turn on/off line echo on the client, if the
                client supports it (e.g. for password input). Remember
                that you must manually activate it again later.

        Notes:
            The telnet TTYPE negotiation flags, if any, are used if no kwargs
            are given.

        """
        ## profiling, debugging
        #if text.startswith("TEST_MESSAGE"): 1/0
        #from evennia.server.profiling.timetrace import timetrace
        #text = timetrace(text, "telnet.data_out", final=True)

        try:
            text = utils.to_str(text if text else "", encoding=self.encoding)
        except Exception as e:
            self.sendLine(str(e))
            return
        if "oob" in kwargs and "OOB" in self.protocol_flags:
            # oob is a list of [(cmdname, arg, kwarg), ...]
            for cmdname, args, okwargs in kwargs["oob"]:
                self.oob.data_out(cmdname, *args, **okwargs)

        # parse **kwargs, falling back to ttype if nothing is given explicitly
        ttype = self.protocol_flags.get('TTYPE', {})
        xterm256 = kwargs.get(
            "xterm256",
            ttype.get('256 COLORS', False) if ttype.get("init_done") else True)
        useansi = kwargs.get(
            "ansi", ttype and ttype.get('ANSI', False)
            if ttype.get("init_done") else True)
        raw = kwargs.get("raw", False)
        nomarkup = kwargs.get("nomarkup", not (xterm256 or useansi))
        prompt = kwargs.get("prompt")
        echo = kwargs.get("echo", None)
        mxp = kwargs.get("mxp", self.protocol_flags.get("MXP", False))

        if raw:
            # no processing whatsoever
            self.sendLine(text)
        elif text:
            # we need to make sure to kill the color at the end in order
            # to match the webclient output.
            linetosend = ansi.parse_ansi(_RE_N.sub("", text) + "{n",
                                         strip_ansi=nomarkup,
                                         xterm256=xterm256,
                                         mxp=mxp)
            if mxp:
                linetosend = mxp_parse(linetosend)
            self.sendLine(linetosend)

        if prompt:
            # Send prompt separately
            prompt = ansi.parse_ansi(_RE_N.sub("", prompt) + "{n",
                                     strip_ansi=nomarkup,
                                     xterm256=xterm256)
            if mxp:
                prompt = mxp_parse(prompt)
            prompt = prompt.replace(IAC, IAC + IAC).replace('\n', '\r\n')
            prompt += IAC + GA
            self.transport.write(mccp_compress(self, prompt))
        if echo:
            self.transport.write(mccp_compress(self, IAC + WONT + ECHO))
        elif echo == False:
            self.transport.write(mccp_compress(self, IAC + WILL + ECHO))
Example #39
0
def protfunc_parser(value,
                    available_functions=None,
                    testing=False,
                    stacktrace=False,
                    **kwargs):
    """
    Parse a prototype value string for a protfunc and process it.

    Available protfuncs are specified as callables in one of the modules of
    `settings.PROTFUNC_MODULES`, or specified on the command line.

    Args:
        value (any): The value to test for a parseable protfunc. Only strings will be parsed for
            protfuncs, all other types are returned as-is.
        available_functions (dict, optional): Mapping of name:protfunction to use for this parsing.
            If not set, use default sources.
        testing (bool, optional): Passed to protfunc. If in a testing mode, some protfuncs may
            behave differently.
        stacktrace (bool, optional): If set, print the stack parsing process of the protfunc-parser.

    Kwargs:
        session (Session): Passed to protfunc. Session of the entity spawning the prototype.
        protototype (dict): Passed to protfunc. The dict this protfunc is a part of.
        current_key(str): Passed to protfunc. The key in the prototype that will hold this value.
        any (any): Passed on to the protfunc.

    Returns:
        testresult (tuple): If `testing` is set, returns a tuple (error, result) where error is
            either None or a string detailing the error from protfunc_parser or seen when trying to
            run `literal_eval` on the parsed string.
        any (any): A structure to replace the string on the prototype level. If this is a
            callable or a (callable, (args,)) structure, it will be executed as if one had supplied
            it to the prototype directly. This structure is also passed through literal_eval so one
            can get actual Python primitives out of it (not just strings). It will also identify
            eventual object #dbrefs in the output from the protfunc.

    """
    if not isinstance(value, basestring):
        try:
            value = value.dbref
        except AttributeError:
            pass
        value = to_str(value, force_string=True)

    available_functions = PROT_FUNCS if available_functions is None else available_functions

    # insert $obj(#dbref) for #dbref
    value = _RE_DBREF.sub("$obj(\\1)", value)

    result = inlinefuncs.parse_inlinefunc(value,
                                          available_funcs=available_functions,
                                          stacktrace=stacktrace,
                                          testing=testing,
                                          **kwargs)

    err = None
    try:
        result = literal_eval(result)
    except ValueError:
        pass
    except Exception as err:
        err = str(err)
    if testing:
        return err, result
    return result
Example #40
0
    def parse_ansi(self, string, strip_ansi=False, xterm256=False, mxp=False):
        """
        Parses a string, subbing color codes according to the stored
        mapping.

        Args:
            string (str): The string to parse.
            strip_ansi (boolean, optional): Strip all found ansi markup.
            xterm256 (boolean, optional): If actually using xterm256 or if
                these values should be converted to 16-color ANSI.
            mxp (boolean, optional): Parse MXP commands in string.

        Returns:
            string (str): The parsed string.

        """
        if hasattr(string, "_raw_string"):
            if strip_ansi:
                return string.clean()
            else:
                return string.raw()

        if not string:
            return ""

        # check cached parsings
        global _PARSE_CACHE
        cachekey = "%s-%s-%s-%s" % (string, strip_ansi, xterm256, mxp)
        if cachekey in _PARSE_CACHE:
            return _PARSE_CACHE[cachekey]

        # pre-convert bright colors to xterm256 color tags
        string = self.brightbg_sub.sub(self.sub_brightbg, string)

        def do_xterm256_fg(part):
            return self.sub_xterm256(part, xterm256, "fg")

        def do_xterm256_bg(part):
            return self.sub_xterm256(part, xterm256, "bg")

        def do_xterm256_gfg(part):
            return self.sub_xterm256(part, xterm256, "gfg")

        def do_xterm256_gbg(part):
            return self.sub_xterm256(part, xterm256, "gbg")

        in_string = utils.to_str(string)

        # do string replacement
        parsed_string = []
        parts = self.ansi_escapes.split(in_string) + [" "]
        for part, sep in zip(parts[::2], parts[1::2]):
            pstring = self.xterm256_fg_sub.sub(do_xterm256_fg, part)
            pstring = self.xterm256_bg_sub.sub(do_xterm256_bg, pstring)
            pstring = self.xterm256_gfg_sub.sub(do_xterm256_gfg, pstring)
            pstring = self.xterm256_gbg_sub.sub(do_xterm256_gbg, pstring)
            pstring = self.ansi_sub.sub(self.sub_ansi, pstring)
            parsed_string.append("%s%s" % (pstring, sep[0].strip()))
        parsed_string = "".join(parsed_string)

        if not mxp:
            parsed_string = self.strip_mxp(parsed_string)

        if strip_ansi:
            # remove all ansi codes (including those manually
            # inserted in string)
            return self.strip_raw_codes(parsed_string)

        # cache and crop old cache
        _PARSE_CACHE[cachekey] = parsed_string
        if len(_PARSE_CACHE) > _PARSE_CACHE_SIZE:
            _PARSE_CACHE.popitem(last=False)

        return parsed_string
Example #41
0
    Bidirectional Server <-> Portal

    Sent when either process needs to call an arbitrary function in
    the other. This does not use the batch-send functionality.

    """
    key = "FunctionCall"
    arguments = [('module', amp.String()), ('function', amp.String()),
                 ('args', amp.String()), ('kwargs', amp.String())]
    errors = {Exception: 'EXCEPTION'}
    response = [('result', amp.String())]


# Helper functions for pickling.

dumps = lambda data: to_str(pickle.dumps(to_str(data), pickle.HIGHEST_PROTOCOL)
                            )
loads = lambda data: pickle.loads(to_str(data))

#------------------------------------------------------------
# Core AMP protocol for communication Server <-> Portal
#------------------------------------------------------------


class AMPProtocol(amp.AMP):
    """
    This is the protocol that the MUD server and the proxy server
    communicate to each other with. AMP is a bi-directional protocol,
    so both the proxy and the MUD use the same commands and protocol.

    AMP specifies responder methods here and connect them to
    amp.Command subclasses that specify the datatypes of the
Example #42
0
def dumps(data):
    return to_str(pickle.dumps(to_str(data), pickle.HIGHEST_PROTOCOL))
Example #43
0
    def send_text(self, *args, **kwargs):
        """
        Send text data. This is an in-band telnet operation.

        Args:
            text (str): The first argument is always the text string to send. No other arguments
                are considered.
        Kwargs:
            options (dict): Send-option flags
                   - mxp: Enforce MXP link support.
                   - ansi: Enforce no ANSI colors.
                   - xterm256: Enforce xterm256 colors, regardless of TTYPE.
                   - noxterm256: Enforce no xterm256 color support, regardless of TTYPE.
                   - nocolor: Strip all Color, regardless of ansi/xterm256 setting.
                   - raw: Pass string through without any ansi processing
                        (i.e. include Evennia ansi markers but do not
                        convert them into ansi tokens)
                   - echo: Turn on/off line echo on the client. Turn
                        off line echo for client, for example for password.
                        Note that it must be actively turned back on again!

        """
        text = args[0] if args else ""
        if text is None:
            return
        text = to_str(text, force_string=True)

        # handle arguments
        options = kwargs.get("options", {})
        flags = self.protocol_flags
        xterm256 = options.get(
            "xterm256",
            flags.get('XTERM256', False) if flags["TTYPE"] else True)
        useansi = options.get(
            "ansi",
            flags.get('ANSI', False) if flags["TTYPE"] else True)
        raw = options.get("raw", flags.get("RAW", False))
        nocolor = options.get(
            "nocolor",
            flags.get("NOCOLOR") or not (xterm256 or useansi))
        echo = options.get("echo", None)
        mxp = options.get("mxp", flags.get("MXP", False))
        screenreader = options.get("screenreader",
                                   flags.get("SCREENREADER", False))

        if screenreader:
            # screenreader mode cleans up output
            text = ansi.parse_ansi(text,
                                   strip_ansi=True,
                                   xterm256=False,
                                   mxp=False)
            text = _RE_SCREENREADER_REGEX.sub("", text)

        if options.get("send_prompt"):
            # send a prompt instead.
            prompt = text
            if not raw:
                # processing
                prompt = ansi.parse_ansi(
                    _RE_N.sub("", prompt) +
                    ("||n" if prompt.endswith("|") else "|n"),
                    strip_ansi=nocolor,
                    xterm256=xterm256)
                if mxp:
                    prompt = mxp_parse(prompt)
            prompt = prompt.replace(IAC, IAC + IAC).replace('\n', '\r\n')
            prompt += IAC + GA
            self.transport.write(mccp_compress(self, prompt))
        else:
            if echo is not None:
                # turn on/off echo. Note that this is a bit turned around since we use
                # echo as if we are "turning off the client's echo" when telnet really
                # handles it the other way around.
                if echo:
                    # by telling the client that WE WON'T echo, the client knows
                    # that IT should echo. This is the expected behavior from
                    # our perspective.
                    self.transport.write(mccp_compress(self,
                                                       IAC + WONT + ECHO))
                else:
                    # by telling the client that WE WILL echo, the client can
                    # safely turn OFF its OWN echo.
                    self.transport.write(mccp_compress(self,
                                                       IAC + WILL + ECHO))
            if raw:
                # no processing
                self.sendLine(text)
                return
            else:
                # we need to make sure to kill the color at the end in order
                # to match the webclient output.
                linetosend = ansi.parse_ansi(
                    _RE_N.sub("", text) +
                    ("||n" if text.endswith("|") else "|n"),
                    strip_ansi=nocolor,
                    xterm256=xterm256,
                    mxp=mxp)
                if mxp:
                    linetosend = mxp_parse(linetosend)
                self.sendLine(linetosend)
Example #44
0
def client_options(session, *args, **kwargs):
    """
    This allows the client an OOB way to inform us about its name and capabilities.
    This will be integrated into the session settings

    Kwargs:
        get (bool): If this is true, return the settings as a dict
            (ignore all other kwargs).
        client (str): A client identifier, like "mushclient".
        version (str): A client version
        ansi (bool): Supports ansi colors
        xterm256 (bool): Supports xterm256 colors or not
        mxp (bool): Supports MXP or not
        utf-8 (bool): Supports UTF-8 or not
        screenreader (bool): Screen-reader mode on/off
        mccp (bool): MCCP compression on/off
        screenheight (int): Screen height in lines
        screenwidth (int): Screen width in characters
        inputdebug (bool): Debug input functions
        nomarkup (bool): Strip markup
        raw (bool): Turn off parsing

    """
    flags = session.protocol_flags
    if not kwargs or kwargs.get("get", False):
        # return current settings
        options = dict((key, flags[key]) for key in flags
                       if key.upper() in ("ANSI", "XTERM256", "MXP", "UTF-8",
                                          "SCREENREADER", "ENCODING", "MCCP",
                                          "SCREENHEIGHT", "SCREENWIDTH",
                                          "INPUTDEBUG", "RAW", "NOMARKUP"))
        session.msg(client_options=options)
        return

    def validate_encoding(val):
        # helper: change encoding
        try:
            to_str(to_unicode("test-string"), encoding=val)
        except LookupError:
            raise RuntimeError("The encoding '|w%s|n' is invalid. " % val)
        return val

    def validate_size(val):
        return {0: int(val)}

    def validate_bool(val):
        if isinstance(val, basestring):
            return True if val.lower() in ("true", "on", "1") else False
        return bool(val)

    for key, value in kwargs.iteritems():
        key = key.lower()
        if key == "client":
            flags["CLIENTNAME"] = to_str(value)
        elif key == "version":
            if "CLIENTNAME" in flags:
                flags["CLIENTNAME"] = "%s %s" % (flags["CLIENTNAME"],
                                                 to_str(value))
        elif key == "ENCODING":
            flags["ENCODING"] = validate_encoding(value)
        elif key == "ansi":
            flags["ANSI"] = validate_bool(value)
        elif key == "xterm256":
            flags["XTERM256"] = validate_bool(value)
        elif key == "mxp":
            flags["MXP"] = validate_bool(value)
        elif key == "utf-8":
            flags["UTF-8"] = validate_bool(value)
        elif key == "screenreader":
            flags["SCREENREADER"] = validate_bool(value)
        elif key == "mccp":
            flags["MCCP"] = validate_bool(value)
        elif key == "screenheight":
            flags["SCREENHEIGHT"] = validate_size(value)
        elif key == "screenwidth":
            flags["SCREENWIDTH"] = validate_size(value)
        elif key == "inputdebug":
            flags["INPUTDEBUG"] = validate_bool(value)
        elif key == "nomarkup":
            flags["NOMARKUP"] = validate_bool(value)
        elif key == "raw":
            flags["RAW"] = validate_bool(value)
        elif key in ('Char 1', 'Char.Skills 1', 'Char.Items 1', 'Room 1',
                     'IRE.Rift 1', 'IRE.Composer 1'):
            # ignore mudlet's default send (aimed at IRE games)
            pass
        elif not key in ("options", "cmdid"):
            err = _ERROR_INPUT.format(name="client_settings",
                                      session=session,
                                      inp=key)
            session.msg(text=err)
    session.protocol_flags = flags
    # we must update the portal as well
    session.sessionhandler.session_portal_sync(session)
Example #45
0
    def test_exit(self):
        """Test the callbacks of an exit."""
        self.char1.key = "char1"
        code = dedent("""
            if character.key == "char1":
                character.msg("You can leave.")
            else:
                character.msg("You cannot leave.")
                deny()
        """.strip("\n"))
        # Enforce self.exit.destination since swapping typeclass lose it
        self.exit.destination = self.room2

        # Try the can_traverse callback
        self.handler.add_callback(self.exit,
                                  "can_traverse",
                                  code,
                                  author=self.char1,
                                  valid=True)

        # Have char1 move through the exit
        self.call(ExitCommand(), "", "You can leave.", obj=self.exit)
        self.assertIs(self.char1.location, self.room2)

        # Have char2 move through this exit
        self.call(ExitCommand(),
                  "",
                  "You cannot leave.",
                  obj=self.exit,
                  caller=self.char2)
        self.assertIs(self.char2.location, self.room1)

        # Try the traverse callback
        self.handler.del_callback(self.exit, "can_traverse", 0)
        self.handler.add_callback(self.exit,
                                  "traverse",
                                  "character.msg('Fine!')",
                                  author=self.char1,
                                  valid=True)

        # Have char2 move through the exit
        self.call(ExitCommand(), "", obj=self.exit, caller=self.char2)
        self.assertIs(self.char2.location, self.room2)
        self.handler.del_callback(self.exit, "traverse", 0)

        # Move char1 and char2 back
        self.char1.location = self.room1
        self.char2.location = self.room1

        # Test msg_arrive and msg_leave
        code = 'message = "{character} goes out."'
        self.handler.add_callback(self.exit,
                                  "msg_leave",
                                  code,
                                  author=self.char1,
                                  valid=True)

        # Have char1 move through the exit
        old_msg = self.char2.msg
        try:
            self.char2.msg = Mock()
            self.call(ExitCommand(), "", obj=self.exit)
            stored_msg = [
                args[0] if args and args[0] else kwargs.get(
                    "text", utils.to_str(kwargs))
                for name, args, kwargs in self.char2.msg.mock_calls
            ]
            # Get the first element of a tuple if msg received a tuple instead of a string
            stored_msg = [
                smsg[0] if isinstance(smsg, tuple) else smsg
                for smsg in stored_msg
            ]
            returned_msg = ansi.parse_ansi("\n".join(stored_msg),
                                           strip_ansi=True)
            self.assertEqual(returned_msg, "char1 goes out.")
        finally:
            self.char2.msg = old_msg

        # Create a return exit
        back = create_object("evennia.objects.objects.DefaultExit",
                             key="in",
                             location=self.room2,
                             destination=self.room1)
        code = 'message = "{character} goes in."'
        self.handler.add_callback(self.exit,
                                  "msg_arrive",
                                  code,
                                  author=self.char1,
                                  valid=True)

        # Have char1 move through the exit
        old_msg = self.char2.msg
        try:
            self.char2.msg = Mock()
            self.call(ExitCommand(), "", obj=back)
            stored_msg = [
                args[0] if args and args[0] else kwargs.get(
                    "text", utils.to_str(kwargs))
                for name, args, kwargs in self.char2.msg.mock_calls
            ]
            # Get the first element of a tuple if msg received a tuple instead of a string
            stored_msg = [
                smsg[0] if isinstance(smsg, tuple) else smsg
                for smsg in stored_msg
            ]
            returned_msg = ansi.parse_ansi("\n".join(stored_msg),
                                           strip_ansi=True)
            self.assertEqual(returned_msg, "char1 goes in.")
        finally:
            self.char2.msg = old_msg
Example #46
0
def do_pickle(data):
    "Perform pickle to string"
    return to_str(dumps(data, protocol=PICKLE_PROTOCOL))
Example #47
0
    def send_text(self, *args, **kwargs):
        """
        Send text data. This is an in-band telnet operation.

        Args:
            text (str): The first argument is always the text string to send. No other arguments
                are considered.
        Kwargs:
            options (dict): Send-option flags
                   - mxp: Enforce MXP link support.
                   - ansi: Enforce no ANSI colors.
                   - xterm256: Enforce xterm256 colors, regardless of TTYPE setting.
                   - nocolor: Strip all colors.
                   - raw: Pass string through without any ansi processing
                        (i.e. include Evennia ansi markers but do not
                        convert them into ansi tokens)
                   - echo: Turn on/off line echo on the client. Turn
                        off line echo for client, for example for password.
                        Note that it must be actively turned back on again!

        """
        # print("telnet.send_text", args,kwargs  # DEBUG)
        text = args[0] if args else ""
        if text is None:
            return
        text = to_str(text, force_string=True)

        # handle arguments
        options = kwargs.get("options", {})
        flags = self.protocol_flags
        xterm256 = options.get("xterm256", flags.get('XTERM256', True))
        useansi = options.get("ansi", flags.get('ANSI', True))
        raw = options.get("raw", flags.get("RAW", False))
        nocolor = options.get(
            "nocolor",
            flags.get("NOCOLOR") or not (xterm256 or useansi))
        # echo = options.get("echo", None)  # DEBUG
        screenreader = options.get("screenreader",
                                   flags.get("SCREENREADER", False))

        if screenreader:
            # screenreader mode cleans up output
            text = ansi.parse_ansi(text,
                                   strip_ansi=True,
                                   xterm256=False,
                                   mxp=False)
            text = _RE_SCREENREADER_REGEX.sub("", text)

        if raw:
            # no processing
            self.sendLine(text)
            return
        else:
            # we need to make sure to kill the color at the end in order
            # to match the webclient output.
            linetosend = ansi.parse_ansi(
                _RE_N.sub("", text) + ("||n" if text.endswith("|") else "|n"),
                strip_ansi=nocolor,
                xterm256=xterm256,
                mxp=False)
            self.sendLine(linetosend)
Example #48
0
def loads(data):
    return pickle.loads(to_str(data))
Example #49
0
def force_str(inp):
    """Helper to shorten code"""
    return to_str(inp, force_string=True)
Example #50
0
def jsonify(obj):
    return utils.to_str(json.dumps(obj, ensure_ascii=False, cls=LazyEncoder))
Example #51
0
 def __init__(self, obj):
     "Initialize handler."
     self.obj = obj
     self._objid = obj.id
     self._model = to_str(obj.__dbclass__.__name__.lower())
     self._cache = None
Example #52
0
        Notes:
            `at_msg_receive` will be called on this Object.
            All extra kwargs will be passed on to the protocol.

        """
        # Send messages to the client. Messages are in format of JSON.
        raw = kwargs.get("raw", False)
        if not raw:
            try:
                text = json.dumps(text)
            except Exception, e:
                text = json.dumps({"err": "There is an error occurred while outputing messages."})
                logger.log_errmsg("json.dumps failed: %s" % e)
        else:
            text = to_str(text, force_string=True) if text != None else ""

        # set raw=True
        if kwargs:
            kwargs["raw"] = True
        else:
            kwargs = {"raw": True}

        # try send hooks
        if from_obj:
            try:
                from_obj.at_msg_send(text=text, to_obj=self, **kwargs)
            except Exception:
                logger.log_trace()
        try:
            if not self.at_msg_receive(text=text, **kwargs):