"""
mssp.py

Adds MSSP support to NakedMud. Add new variables with register_mssp_var.
"""
import hooks, mudsock, telnetlib, types, time



################################################################################
# variables
################################################################################

# the character for MSSP
MSSP     = 'F'    # "%c" % 70

# markers for sending MSSP info
MSSP_VAR = '\x01' # "%c" % 1
MSSP_VAL = '\x02' # "%c" % 2

# handshake crap
server_will_mssp  = telnetlib.IAC + telnetlib.WILL + MSSP
client_do_mssp    = telnetlib.IAC + telnetlib.DO   + MSSP

# subnegotion crap
subneg_start_mssp = telnetlib.IAC + telnetlib.SB   + MSSP
subneg_end_mssp   = telnetlib.IAC + telnetlib.SE

# a table of MSSP variables and functions to build their values
mssp_variables    = { }



################################################################################
# functions
################################################################################
def register_mssp_var(var, val):
    """register a new variable to build info for when we are sending MSSP info.
       val should either a function that builds the arguments, or something
       that can be cast as a string. If val is a string, it should return
       either something that can be cast as a string or, if multiple values are
       acceptable (e.g., in the case of ports) a list of things that can be cast
       as a string.
    """
    mssp_variables[var] = val



################################################################################
# local functions
################################################################################
def build_one_mssp_info(var, val):
    """returns MSSP info for one var:val pair.
    """
    # first, if it's a function, figure out what it returns
    if type(val) == types.FunctionType:
        val = val()

    # next, if the return was a list, turn everything to strings for join()
    if type(val) == list:
        val = MSSP_VAL.join([str(x) for x in val])

    # return our info
    return MSSP_VAR + var + MSSP_VAL + str(val)

def build_mssp_info():
    """returns a string for our current MSSP info.
    """
    buf=[build_one_mssp_info(var,val) for var,val in mssp_variables.iteritems()]
    return subneg_start_mssp + "".join(buf) + subneg_end_mssp



################################################################################
# hooks
################################################################################
def will_mssp_hook(info):
    """connection has been received. See if they are an MSSP crawler.
    """
    sock, = hooks.parse_info(info)
    sock.send_raw(server_will_mssp)

def do_mssp_hook(info):
    """check to see if the iac command was a signal that the client handles MSSP
    """
    sock, cmd = hooks.parse_info(info)
    if cmd == client_do_mssp:
        sock.send_raw(build_mssp_info())
        


################################################################################
# initialization
################################################################################
hooks.add("receive_iac",        do_mssp_hook)
hooks.add("receive_connection", will_mssp_hook)

# register all of our default MSSP variables
register_mssp_var("PLAYERS",  lambda : len(mudsock.socket_list()))
register_mssp_var("UPTIME",   int(time.time()))
register_mssp_var("FAMILY",   "Custom")
register_mssp_var("ANSI",     1)