mccp module
"S.Aeschbacher" <[email protected]> Sun, 12 Dec 2004 23:48:16 +0100
| Newsgroups | gmane.comp.games.mud.client.lyntin |
|---|---|
| Message-ID | <[email protected]> |
Hi I hacked together a module which would offer MCCP functionality. On the server I use it SOMETIMES works (with MCCPv2). One problem I see is the following: According to the specification of MCCP [1], the server starts to send compressed data directly after having sent the telnet option (IAC SB COMPRESS2 IAC SE for MCCPv2) which indicates the start of the compressed data. So this option can be at the start of a chunk of data that is compressed (all except the option). The problem is, _filterIncomingData has already been called when the handleNego method is running. When this first part is not sent through the decompressor object, the whole stream is out of sync and nothing works... At the moment I don't see how I can repass the data through the decompressor. Any hints or ideas? Or am I searching in the wrong place? regards Stefan [1] (http://mccp.afkmud.com/protocol.html)
mccp.py
(text/plain, 3.1 KB)
from lyntin import manager, exported, constants
import zlib
IAC = chr(255)
DONT = chr(254)
DO = chr(253)
WONT = chr(252)
WILL = chr(251)
SB = chr(250)
SE = chr(240)
COMPRESS = chr(85)
COMPRESS2 = chr(86)
do_debug = True
def debug(ses, msg):
if do_debug:
exported.write_message('##MCCP## ' + ses.getName() + ' ' + msg)
class MCCPManager(manager.Manager):
def __init__(self):
self._sessions = {}
def decompress_data(self, args):
ses = args['session']
dataadj = args['dataadj']
if not self._sessions.has_key(ses):
debug(ses, 'Unknown session')
return dataadj
decompressor = self._sessions[ses]['DECOMPRESS']
if decompressor != None:
try:
newdataadj = decompressor.decompress(dataadj)
debug(ses, 'Successfully decompressed')
return newdataadj
except:
debug(ses, 'An error occured during MCCP decompression')
return dataadj
def addSession(self, newsession, basession=None):
# XXX basesession ignored for the moment
debug(newsession, 'New Session Created')
session_state = {}
session_state['MCCPv1'] = False
session_state['MCCPv2'] = False
session_state['DECOMPRESS'] = None
self._sessions[newsession] = session_state
def removeSession(self, ses):
self._sessions.pop(ses)
def handle_telnet_options(self, args):
ses = args['session']
option = args['data']
if not self._sessions.has_key(ses):
debug(ses, 'Unknown session')
return
session_state = self._sessions[ses]
if option == IAC + WILL + COMPRESS:
# Negotiate MCCPv1
# If MCCPv2 has been negotiated beforehand (and
# according to the protocol definition it has to come
# first), MCCPv1 is denied.
if session_state['MCCPv2'] == True:
debug(ses, 'Denied MCCPv1, MCCPv2 already here')
ses._socket.write(IAC + DONT + COMPRESS, 0)
raise exported.StopSpammingException()
else:
debug(ses, 'Accepting MCCPv1')
ses._socket.write(IAC + DO + COMPRESS, 0)
session_state['MCCPv1'] = True
raise exported.StopSpammingException()
elif option == IAC + WILL + COMPRESS2:
# Negotiate MCCPv2
# MCCPv2 negotiation should always be before MCCPv1
debug(ses, 'Accepting MCCPv2')
ses._socket.write(IAC + DO + COMPRESS2, 0)
session_state['MCCPv2'] = True
raise exported.StopSpammingException()
elif option == IAC + SB + COMPRESS + WILL + SE:
if session_state['MCCPv1'] == True:
debug(ses, 'Starting decompression via MCCPv1')
session_state['DECOMPRESS'] = zlib.decompressobj()
raise exported.StopSpammingException()
elif option == IAC + SB + COMPRESS2 + IAC + SE:
if session_state['MCCPv2'] == True:
debug(ses, 'Starting decompression via MCCPv2')
session_state['DECOMPRESS'] = zlib.decompressobj()
raise exported.StopSpammingException()
def load():
global mm
mm = MCCPManager()
exported.add_manager('MCCP', mm)
exported.hook_register("net_handle_telnet_option", mm.handle_telnet_options, constants.FIRST)
exported.hook_register("net_read_data_filter", mm.decompress_data, constants.FIRST)
def unload():
global mm
exported.hook_unregister("net_handle_telnet_option", mm.handle_telnet_options)
exported.hook_unregister("net_read_data_filter", mm.decompress_data)