OOB passing data client->server. A few more default commands are needed.
This commit is contained in:
parent
96c6ad4aff
commit
16bbe009c3
6 changed files with 103 additions and 48 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"))
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue