evennia/src/server/ttype.py

115 lines
4.3 KiB
Python

"""
TTYPE (MTTS) - Mud Terminal Type Standard
This module implements the TTYPE telnet protocol as per
http://tintin.sourceforge.net/mtts/. It allows the server to ask the
client about its capabilities. If the client also supports TTYPE, it
will return with information such as its name, if it supports colour
etc. If the client does not support TTYPE, this will be ignored.
All data will be stored on the protocol's protocol_flags dictionary,
under the 'TTYPE' key.
"""
# telnet option codes
TTYPE = chr(24)
IS = chr(0)
SEND = chr(1)
# terminal capabilities and their codes
MTTS = [(128,'PROXY'),
(64, 'SCREEN READER'),
(32, 'OSC COLOR PALETTE'),
(16, 'MOUSE TRACKING'),
(8, '256 COLORS'),
(4, 'UTF-8'),
(2, 'VT100'),
(1, 'ANSI')]
# some clients sends erroneous strings instead
# of capability numbers. We try to convert back.
MTTS_invert = {"PROXY":128,
"SCREEN COLOR PALETTE":64,
"OSC COLOR PALETTE": 32,
"MOUSE TRACKING": 16,
"256 COLORS": 8,
"UTF-8": 4,
"VT100": 2,
"ANSI": 1}
class Ttype(object):
"""
Handles ttype negotiations. Called and initiated by the
telnet protocol.
"""
def __init__(self, protocol):
"""
initialize ttype by storing protocol on ourselves and calling
the client to see if it supporst ttype.
the ttype_step indicates how far in the data retrieval we've
gotten.
"""
self.ttype_step = 0
self.protocol = protocol
self.protocol.protocol_flags['TTYPE'] = {"init_done":False}
# setup protocol to handle ttype initialization and negotiation
self.protocol.negotiationMap[TTYPE] = self.do_ttype
# ask if client will ttype, connect callback if it does.
self.protocol.will(TTYPE).addCallbacks(self.do_ttype, self.no_ttype)
def no_ttype(self, option):
"""
Callback if ttype is not supported by client.
"""
self.protocol.protocol_flags['TTYPE'] = {"init_done":True}
def do_ttype(self, option):
"""
Handles negotiation of the ttype protocol once the
client has confirmed that it supports the ttype
protocol.
The negotiation proceeds in several steps, each returning a
certain piece of information about the client. All data is
stored on protocol.protocol_flags under the TTYPE key.
"""
if self.protocol.protocol_flags['TTYPE']['init_done']:
return
self.ttype_step += 1
if self.ttype_step == 1:
# set up info storage and initialize subnegotiation
self.protocol.requestNegotiation(TTYPE, SEND)
else:
# receive data
option = "".join(option).lstrip(IS)
if self.ttype_step == 2:
self.protocol.protocol_flags['TTYPE']['CLIENTNAME'] = option
self.protocol.requestNegotiation(TTYPE, SEND)
elif self.ttype_step == 3:
self.protocol.protocol_flags['TTYPE']['TERM'] = option
self.protocol.requestNegotiation(TTYPE, SEND)
elif self.ttype_step == 4:
try:
option = int(option.strip('MTTS '))
except ValueError:
# it seems some clients don't send MTTS according to protocol
# specification, but instead just sends the data as plain
# strings. We try to convert back.
option = MTTS_invert.get(option.strip('MTTS ').upper())
if not option:
# no conversion possible. Give up.
self.protocol.protocol_flags['TTYPE']['init_done'] = True
return
self.protocol.protocol_flags['TTYPE']['MTTS'] = option
for codenum, standard in MTTS:
if option == 0:
break
status = option % codenum < option
self.protocol.protocol_flags['TTYPE'][standard] = status
if status:
option = option % codenum
self.protocol.protocol_flags['TTYPE']['init_done'] = True
#print "ttype results:", self.protocol.protocol_flags['TTYPE']