OOB passing data client->server. A few more default commands are needed.

This commit is contained in:
Griatch 2013-10-07 19:57:01 +02:00
parent 96c6ad4aff
commit 16bbe009c3
6 changed files with 103 additions and 48 deletions

View file

@ -26,10 +26,10 @@ from django.conf import settings
from src.server.models import ServerConfig from src.server.models import ServerConfig
from src.server.sessionhandler import SESSIONS from src.server.sessionhandler import SESSIONS
from src.scripts.scripts import Script from src.scripts.scripts import Script
from src.create import create_script from src.utils.create import create_script
from src.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj from src.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj
from src.utils import logger from src.utils import logger
from src.utils.utils import variable_from_module, to_str from src.utils.utils import variable_from_module, to_str, is_iter, make_iter
_SA = object.__setattr__ _SA = object.__setattr__
_GA = object.__getattribute__ _GA = object.__getattribute__
@ -307,7 +307,7 @@ class OOBHandler(object):
oob_tracker_name = "_track_db_value_change" oob_tracker_name = "_track_db_value_change"
self.track(attrobj, tracker_key, attr_name, sessid, property_name=oob_tracker_name) self.track(attrobj, tracker_key, attr_name, sessid, property_name=oob_tracker_name)
def run(self, func_key, *args, **kwargs): def execute_cmd(self, func_key, *args, **kwargs):
""" """
Retrieve oobfunc from OOB_FUNCS and execute it immediately Retrieve oobfunc from OOB_FUNCS and execute it immediately
using *args and **kwargs using *args and **kwargs

View file

@ -132,7 +132,7 @@ class Msdp(object):
""" """
self.protocol = protocol self.protocol = protocol
self.protocol.protocol_flags['MSDP'] = False self.protocol.protocol_flags['MSDP'] = False
self.protocol.negotiationMap[MSDP] = self.msdp_to_func self.protocol.negotiationMap[MSDP] = self.msdp_to_evennia
self.protocol.will(MSDP).addCallbacks(self.do_msdp, self.no_msdp) self.protocol.will(MSDP).addCallbacks(self.do_msdp, self.no_msdp)
self.msdp_reported = {} self.msdp_reported = {}
@ -148,10 +148,7 @@ class Msdp(object):
print "msdp supported" print "msdp supported"
self.protocol.protocol_flags['MSDP'] = True self.protocol.protocol_flags['MSDP'] = True
def parse_msdp(self, args): def evennia_to_msdp(self, cmdname, data):
"Called with arguments to subnegotiation"
def func_to_msdp(self, cmdname, data):
""" """
handle return data from cmdname by converting it to handle return data from cmdname by converting it to
a proper msdp structure. data can either be a single value (will be a proper msdp structure. data can either be a single value (will be
@ -179,6 +176,7 @@ class Msdp(object):
def make_array(name, datalist, string): def make_array(name, datalist, string):
"build a simple array. Arrays may not nest tables by definition." "build a simple array. Arrays may not nest tables by definition."
print "make_array", datalist, string
string += MSDP_VAR + name + MSDP_ARRAY_OPEN string += MSDP_VAR + name + MSDP_ARRAY_OPEN
for val in datalist: for val in datalist:
string += MSDP_VAL + val string += MSDP_VAL + val
@ -190,11 +188,11 @@ class Msdp(object):
elif hasattr(data, '__iter__'): elif hasattr(data, '__iter__'):
msdp_string = make_array(cmdname, data, "") msdp_string = make_array(cmdname, data, "")
else: else:
msdp_string = MSDP_VAR + cmdname + MSDP_VAL + data msdp_string = MSDP_VAR + cmdname + MSDP_VAL + data if data!=None else ""
return msdp_string return msdp_string
def msdp_to_func(self, data): def msdp_to_evennia(self, data):
""" """
Handle a client's requested negotiation, converting Handle a client's requested negotiation, converting
it into a function mapping - either one of the MSDP it into a function mapping - either one of the MSDP
@ -229,57 +227,55 @@ class Msdp(object):
print "MSDP: table, array, variables:", tables, arrays, variables print "MSDP: table, array, variables:", tables, arrays, variables
# all variables sent through msdp to Evennia are considered commands with arguments.
# there are three forms of commands possible through msdp:
#
# VARNAME VAR -> varname(var)
# ARRAYNAME VAR VAL VAR VAL VAR VAL ENDARRAY -> arrayname(val,val,val)
# TABLENAME TABLE VARNAME VAL VARNAME VAL ENDTABLE -> tablename(varname=val, varname=val)
#
ret = "" ret = ""
# default MSDP functions # default MSDP functions
if "LIST" in variables: if "LIST" in variables:
ret += self.func_to_msdp("LIST", self.msdp_cmd_list(variables["LIST"])) ret += self.evennia_to_msdp("LIST", self.msdp_cmd_list(*(variables.pop("LIST"),)))
del variables["LIST"]
if "REPORT" in variables: if "REPORT" in variables:
ret += self.func_to_msdp("REPORT", self.msdp_cmd_report(*(variables["REPORT"],))) ret += self.evennia_to_msdp("REPORT", self.msdp_cmd_report(*(variables.pop("REPORT"),)))
del variables["REPORT"]
if "REPORT" in arrays: if "REPORT" in arrays:
ret += self.func_to_msdp("REPORT", self.msdp_cmd_report(*arrays["REPORT"])) ret += self.evennia_to_msdp("REPORT", self.msdp_cmd_report(*arrays.pop("REPORT")))
del arrays["REPORT"]
if "RESET" in variables: if "RESET" in variables:
ret += self.func_to_msdp("RESET", self.msdp_cmd_reset(*(variables["RESET"],))) ret += self.evennia_to_msdp("RESET", self.msdp_cmd_reset(*(variables.pop("RESET"),)))
del variables["RESET"]
if "RESET" in arrays: if "RESET" in arrays:
ret += self.func_to_msdp("RESET", self.msdp_cmd_reset(*arrays["RESET"])) ret += self.evennia_to_msdp("RESET", self.msdp_cmd_reset(*arrays.pop("RESET",)))
del arrays["RESET"]
if "SEND" in variables: if "SEND" in variables:
ret += self.func_to_msdp("SEND", self.msdp_cmd_send(*(variables["SEND"],))) ret += self.evennia_to_msdp("SEND", self.msdp_cmd_send(*(variables.pop("SEND",))))
del variables["SEND"]
if "SEND" in arrays: if "SEND" in arrays:
ret += self.func_to_msdp("SEND",self.msdp_cmd_send(*arrays["SEND"])) ret += self.evennia_to_msdp("SEND",self.msdp_cmd_send(*arrays.pop("SEND")))
del arrays["SEND"]
# if there are anything left we look for a custom function # if there are anything left we look for a custom function
for varname, var in variables.items(): for varname, var in variables.items():
# a simple function + argument # a simple function + argument
ooc_func = MSDP_COMMANDS_CUSTOM.get(varname.upper()) ooc_func = MSDP_COMMANDS_CUSTOM.get(varname.upper())
if ooc_func: if ooc_func:
ret += self.func_to_msdp(varname, ooc_func(var)) ret += self.evennia_to_msdp(varname, ooc_func(var))
for arrayname, array in arrays.items(): for arrayname, array in arrays.items():
# we assume the array are multiple arguments to the function # we assume the array are multiple arguments to the function
ooc_func = MSDP_COMMANDS_CUSTOM.get(arrayname.upper()) ooc_func = MSDP_COMMANDS_CUSTOM.get(arrayname.upper())
if ooc_func: if ooc_func:
ret += self.func_to_msdp(arrayname, ooc_func(*array)) ret += self.evennia_to_msdp(arrayname, ooc_func(*array))
for tablename, table in tables.items(): for tablename, table in tables.items():
# we assume tables are keyword arguments to the function # we assume tables are keyword arguments to the function
ooc_func = MSDP_COMMANDS_CUSTOM.get(arrayname.upper()) ooc_func = MSDP_COMMANDS_CUSTOM.get(arrayname.upper())
if ooc_func: if ooc_func:
ret += self.func_to_msdp(tablename, ooc_func(**table)) ret += self.evennia_to_msdp(tablename, ooc_func(**table))
# return any result
if ret: if ret:
# send return value if it exists self.data_out(ret)
self.msdp_send(ret)
ret = IAC + SB + MSDP + ret + IAC + SE
#ret = IAC + SB + MSDP + MSDP_VAR + "SEND" + MSDP_VAL + "Testsend" + IAC + SE
self.protocol._write(ret)
logger.log_infomsg("MSDP_RESULT: %s" % ret)
def msdp_send(self, msdp_string): def data_out(self, msdp_string):
""" """
Return a msdp-valid subnegotiation across the protocol. Return a msdp-valid subnegotiation across the protocol.
""" """
@ -295,21 +291,21 @@ class Msdp(object):
The List command allows for retrieving various info about the server/client The List command allows for retrieving various info about the server/client
""" """
if arg == 'COMMANDS': if arg == 'COMMANDS':
return self.func_to_msdp(arg, MSDP_COMMANDS) return self.evennia_to_msdp(arg, MSDP_COMMANDS)
elif arg == 'LISTS': elif arg == 'LISTS':
return self.func_to_msdp(arg, ("COMMANDS", "LISTS", "CONFIGURABLE_VARIABLES", return self.evennia_to_msdp(arg, ("COMMANDS", "LISTS", "CONFIGURABLE_VARIABLES",
"REPORTED_VARIABLES", "SENDABLE_VARIABLES")) "REPORTED_VARIABLES", "SENDABLE_VARIABLES"))
elif arg == 'CONFIGURABLE_VARIABLES': elif arg == 'CONFIGURABLE_VARIABLES':
return self.func_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID")) return self.evennia_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID"))
elif arg == 'REPORTABLE_VARIABLES': elif arg == 'REPORTABLE_VARIABLES':
return self.func_to_msdp(arg, MSDP_REPORTABLE.keys()) return self.evennia_to_msdp(arg, MSDP_REPORTABLE.keys())
elif arg == 'REPORTED_VARIABLES': elif arg == 'REPORTED_VARIABLES':
# the dynamically set items to report # the dynamically set items to report
return self.func_to_msdp(arg, self.msdp_reported.keys()) return self.evennia_to_msdp(arg, self.msdp_reported.keys())
elif arg == 'SENDABLE_VARIABLES': elif arg == 'SENDABLE_VARIABLES':
return self.func_to_msdp(arg, MSDP_SENDABLE.keys()) return self.evennia_to_msdp(arg, MSDP_SENDABLE.keys())
else: else:
return self.func_to_msdp("LIST", arg) return self.evennia_to_msdp("LIST", arg)
# default msdp commands # default msdp commands

View file

@ -138,6 +138,7 @@ class PortalSessionHandler(SessionHandler):
for session in self.sessions.values(): for session in self.sessions.values():
session.data_out(message) session.data_out(message)
def data_out(self, sessid, text=None, **kwargs): def data_out(self, sessid, text=None, **kwargs):
""" """
Called by server for having the portal relay messages and data Called by server for having the portal relay messages and data

View file

@ -13,6 +13,7 @@ from src.server.session import Session
from src.server.portal import ttype, mssp, msdp from src.server.portal import ttype, mssp, msdp
from src.server.portal.mccp import Mccp, mccp_compress, MCCP from src.server.portal.mccp import Mccp, mccp_compress, MCCP
from src.utils import utils, ansi, logger from src.utils import utils, ansi, logger
from src.utils.utils import make_iter, is_iter
_RE_N = re.compile(r"\{n$") _RE_N = re.compile(r"\{n$")
@ -36,14 +37,13 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
# negotiate ttype (client info) # negotiate ttype (client info)
#self.ttype = ttype.Ttype(self) #self.ttype = ttype.Ttype(self)
# negotiate mssp (crawler communication) # negotiate mssp (crawler communication)
self.mssp = mssp.Mssp(self) #self.mssp = mssp.Mssp(self)
# msdp # msdp
#self.msdp = msdp.Msdp(self) self.msdp = msdp.Msdp(self)
# add this new connection to sessionhandler so # add this new connection to sessionhandler so
# the Server becomes aware of it. # the Server becomes aware of it.
self.sessionhandler.connect(self) self.sessionhandler.connect(self)
def enableRemote(self, option): def enableRemote(self, option):
""" """
This sets up the remote-activated options we allow for this protocol. This sets up the remote-activated options we allow for this protocol.
@ -69,7 +69,6 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
else: else:
return super(TelnetProtocol, self).disableLocal(option) return super(TelnetProtocol, self).disableLocal(option)
def connectionLost(self, reason): def connectionLost(self, reason):
""" """
This is executed when the connection is lost for This is executed when the connection is lost for
@ -163,6 +162,16 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
except Exception, e: except Exception, e:
self.sendLine(str(e)) self.sendLine(str(e))
return return
if "oob" in kwargs:
oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob"))
if "MSDP" in self.protocol_flags:
print "oobstruct:", oobstruct
for cmdname, args in oobstruct:
print "cmdname, args:", cmdname, args
msdp_string = self.msdp.func_to_msdp(cmdname, args)
print "msdp_string:", msdp_string
self.msdp.data_out(msdp_string)
ttype = self.protocol_flags.get('TTYPE', {}) ttype = self.protocol_flags.get('TTYPE', {})
raw = kwargs.get("raw", False) raw = kwargs.get("raw", False)
nomarkup = not (ttype or ttype.get('256 COLORS') or ttype.get('ANSI') or not ttype.get("init_done")) nomarkup = not (ttype or ttype.get('256 COLORS') or ttype.get('ANSI') or not ttype.get("init_done"))

View file

@ -20,6 +20,7 @@ from src.server.session import Session
IDLE_COMMAND = settings.IDLE_COMMAND IDLE_COMMAND = settings.IDLE_COMMAND
_GA = object.__getattribute__ _GA = object.__getattribute__
_ObjectDB = None _ObjectDB = None
_OOB_HANDLER = None
# load optional out-of-band function module # load optional out-of-band function module
OOB_PLUGIN_MODULE = settings.OOB_PLUGIN_MODULE OOB_PLUGIN_MODULE = settings.OOB_PLUGIN_MODULE
@ -178,8 +179,13 @@ class ServerSession(Session):
cmdhandler.cmdhandler(self, text, callertype="session", sessid=self.sessid) cmdhandler.cmdhandler(self, text, callertype="session", sessid=self.sessid)
self.update_session_counters() self.update_session_counters()
if "oob" in kwargs: if "oob" in kwargs:
# relay to OOB handler # handle oob instructions
pass global _OOB_HANDLER
if not _OOB_HANDLER:
from src.servever.oobhandler import OOB_HANDLER as _OOB_HANDLER
oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob", None))
for (funcname, args, kwargs) in oobstruct:
_OOBHANDLER.execute_cmd(funcname, *args, **kwargs)
execute_cmd = data_in # alias execute_cmd = data_in # alias

View file

@ -27,6 +27,7 @@ _ServerSession = None
_ServerConfig = None _ServerConfig = None
_ScriptDB = None _ScriptDB = None
# AMP signals # AMP signals
PCONN = chr(1) # portal session connect PCONN = chr(1) # portal session connect
PDISCONN = chr(2) # portal session disconnect PDISCONN = chr(2) # portal session disconnect
@ -357,6 +358,49 @@ class ServerSessionHandler(SessionHandler):
return self.sessions.get(sessid) return self.sessions.get(sessid)
return None return None
def oobstruct_parser(self, oobstruct):
"""
Helper method for each session to use to parse oob structures
(The 'oob' kwarg of the msg() method)
allowed oob structures are
cmdname
(cmdname, cmdname)
(cmdname,(arg, ))
(cmdname,(arg1,arg2))
(cmdname,{key:val,key2:val2})
(cmdname, (args,), {kwargs})
((cmdname, (arg1,arg2)), cmdname, (cmdname, (arg1,)))
outputs an ordered structure on the form
((cmdname, (args,), {kwargs}), ...), where the two last parts of each tuple may be empty
"""
slen = len(oobstruct)
if not oobstruct:
return tuple(None, (), {})
elif not hasattr(oobstruct, "__iter__"):
# a singular command name, without arguments or kwargs
return (oobstruct.lower(), (), {})
# regardless of number of args/kwargs, the first element must be the function name.
# we will not catch this error if not, but allow it to propagate.
if slen == 1:
return (oobstruct[0].lower(), (), {})
elif slen == 2:
if isinstance(oobstruct[1], dict):
# cmdname, {kwargs}
return (oobstruct[0].lower(), (), dict((key.lower(), val) for key,val in oobstruct[1].items()))
elif isinstance(oobstruct[1], (tuple, list)):
# cmdname, (args,)
return (oobstruct[0].lower(), tuple(arg.lower() for arg in oobstruct[1]), {})
else:
# cmdname, (args,), {kwargs}
return (oobstruct[0].lower(), tuple(arg.lower for arg in oobstruct[1]),
dict((key.lower(), val) for key, val in oobstruct[2].items()))
# either multiple funcnames or multiple func tuples; descend recursively
out = []
for oobpart in oobstruct:
out.append(self.oobstruct_parser(oobpart)[0])
return tuple(out)
def announce_all(self, message): def announce_all(self, message):
""" """
Send message to all connected sessions Send message to all connected sessions
@ -379,5 +423,4 @@ class ServerSessionHandler(SessionHandler):
if session: if session:
session.data_in(text=text, **kwargs) session.data_in(text=text, **kwargs)
SESSIONS = ServerSessionHandler() SESSIONS = ServerSessionHandler()