Permalink
Browse files

support for contact list packet

  • Loading branch information...
1 parent 6e25bc3 commit ec463f12eacfbc047bc7f9a525bfade019f2c9f4 @smirn0v committed May 7, 2012
Showing with 204 additions and 57 deletions.
  1. +66 −27 mrim-bot/{mrim_protocol.py → mmp_protocol.py}
  2. +136 −28 mrim-bot/mmpbase.py
  3. +2 −2 mrim-bot/mrim_bot.py
@@ -3,25 +3,25 @@
from twisted.persisted import styles
from twisted.protocols import basic
-class MRIMBaseHandler(object):
+class MMPBaseHandler(object):
def __init__(self, protocol):
self.auto_remove_handler = True
self.protocol = protocol
-class MRIMLogin2AckHandler(MRIMBaseHandler):
+class MMPLogin2AckHandler(MMPBaseHandler):
def __init__(self, protocol, seq):
- super(MRIMLogin2AckHandler,self).__init__(protocol)
+ super(MMPLogin2AckHandler,self).__init__(protocol)
self.seq = seq
def canHandlePacket(self,packet):
return packet.header.seq == self.seq and isinstance(packet,MMPServerLoginAckPacket)
def handlePacket(self,packet):
- print "Login ack received"
+ print "[+] Logged in"
-class MRIMLogin2RejHandler(MRIMBaseHandler):
+class MMPLogin2RejHandler(MMPBaseHandler):
def __init__(self,protocol,seq):
- super(MRIMLogin2RejHandler,self).__init__(protocol)
+ super(MMPLogin2RejHandler,self).__init__(protocol)
self.seq = seq
def canHandlePacket(self,packet):
@@ -30,22 +30,50 @@ def canHandlePacket(self,packet):
def handlePacket(self,packet):
print "[-] Login rejected: %s"%packet.reason
-class MRIMHelloAckHandler(MRIMBaseHandler):
+class MMPHelloAckHandler(MMPBaseHandler):
def __init__(self, protocol, seq):
- super(MRIMHelloAckHandler,self).__init__(protocol)
+ super(MMPHelloAckHandler,self).__init__(protocol)
self.seq = seq
def canHandlePacket(self,packet):
return packet.header.seq == self.seq and isinstance(packet,MMPServerHelloAckPacket)
def handlePacket(self,packet):
+ self.protocol.startHeartbeat(packet.interval)
header = self.protocol.createHeader()
packet = MMPClientLogin2Packet(header,"johann-the-builder@mail.ru","buildpleasemail")
- self.protocol.addHandler(MRIMLogin2RejHandler(self.protocol,header.seq))
- self.protocol.addHandler(MRIMLogin2AckHandler(self.protocol,header.seq))
+ self.protocol.addHandler(MMPLogin2RejHandler(self.protocol,header.seq))
+ self.protocol.addHandler(MMPLogin2AckHandler(self.protocol,header.seq))
self.protocol.sendPacket(packet)
-class MRIMDispatcherMixin(object):
+class MMPMessageAckHandler(MMPBaseHandler):
+ def __init__(self,protocol):
+ super(MMPMessageAckHandler,self).__init__(protocol)
+ self.auto_remove_handler = False
+
+ def canHandlePacket(self,packet):
+ return isinstance(packet,MMPServerMessageAckPacket)
+
+ def handlePacket(self,packet):
+ header = self.protocol.createHeader()
+ header.seq = packet.header.seq
+ msgReceivedPacket = MMPClientMessageRecv(header,packet.from_email,packet.msgid)
+ self.protocol.sendPacket(msgReceivedPacket)
+
+class MMPIncomingAuthorizationHandler(MMPBaseHandler):
+ def __init__(self,protocol):
+ super(MMPMessageAckHandler,self).__init__(protocol)
+ self.auto_remove_handler = False
+
+ def canHandlePacket(self,packet):
+ return packet.header.seq == self.seq and \
+ isinstance(packet,MMPServerMessageAckPacket) and \
+ packet.flag_set(MESSAGE_FLAG_AUTHORIZE)
+
+ def handlePacket(self,packet):
+ print "[+] Authorization request received from %s"%packet.from_email
+
+class MMPDispatcherMixin(object):
def addHandler(self,handler):
self.handlers += [handler]
@@ -61,37 +89,39 @@ def handlePacket(self,header,payload):
if not packet: return
- print "[+] Packet parsed"
-
packetHandlers = [h for h in self.handlers if h.canHandlePacket(packet)]
for handler in packetHandlers:
handler.handlePacket(packet)
if handler.auto_remove_handler:
self.handlers.remove(handler)
-class MRIMMode:
+class MMPMode:
Header= 1
Body= 2
-class MRIMProtocol(protocol.Protocol,MRIMDispatcherMixin):
+class MMPProtocol(protocol.Protocol,MMPDispatcherMixin):
"""
- MRIM protocol implementation
+ MMP protocol implementation
"""
def __init__(self):
self.handlers = []
self.supported_server_packets = [MMPServerHelloAckPacket,
MMPServerLoginAckPacket,
- MMPServerLoginRejPacket]
+ MMPServerLoginRejPacket,
+ MMPServerMessageAckPacket,
+ MMPServerContactListPacket]
self.buffer = ""
- self.mode = MRIMMode.Header
+ self.mode = MMPMode.Header
self.seq = 1
+
+ self.addHandler(MMPMessageAckHandler(self))
def connectionMade(self):
print "[+] Connected"
packet = MMPClientHelloPacket(self.createHeader())
- self.addHandler(MRIMHelloAckHandler(self,packet.header.seq))
+ self.addHandler(MMPHelloAckHandler(self,packet.header.seq))
self.sendPacket(packet)
def connectionLost(self,reason):
@@ -100,8 +130,8 @@ def connectionLost(self,reason):
def dataReceived(self,data):
self.buffer += data
handlers = {
- MRIMMode.Header: self._extractHeader,
- MRIMMode.Body: self._extractBody
+ MMPMode.Header: self._extractHeader,
+ MMPMode.Body: self._extractBody
}
handlers[self.mode]()
@@ -113,16 +143,22 @@ def createHeader(self):
def sendPacket(self,packet):
self.transport.write(packet.binary_data())
- def startHeartBeat(self,interval):
- pass
+ def startHeartbeat(self,interval):
+ heartbeat = task.LoopingCall(self._sendHeartbeat)
+ heartbeat.start(interval, now = False)
+
+ def _sendHeartbeat(self):
+ header = self.createHeader()
+ packet = MMPClientPingPacket(header)
+ self.sendPacket(packet)
def _extractHeader(self):
if len(self.buffer) < MMPHeader.size:
return
header_data = self.buffer[:MMPHeader.size]
self.buffer = self.buffer[MMPHeader.size:]
self.header = MMPHeader.from_binary_data(header_data)
- self.mode = MRIMMode.Body
+ self.mode = MMPMode.Body
print "[+] Header received %s"%self.header
self._extractBody()
@@ -132,10 +168,13 @@ def _extractBody(self):
print "[+] Body received, len = %d"%self.header.dlen
payload = self.buffer[:self.header.dlen]
self.buffer = self.buffer[self.header.dlen:]
- self.mode = MRIMMode.Header
+ self.mode = MMPMode.Header
+
+ print "[+] Payload: %s"%payload.encode('hex')
+
self.handlePacket(self.header,payload)
self._extractHeader()
-class MRIMClientFactory(protocol.ClientFactory):
+class MMPClientFactory(protocol.ClientFactory):
def buildProtocol(self, address):
- return MRIMProtocol()
+ return MMPProtocol()
View
@@ -9,18 +9,6 @@ class MMPWrongHeaderData(Exception):
class MMPMalformedPacket(Exception):
pass
-def unpack_lps(data):
- size_length = struct.calcsize('I')
- if len(data) < size_length:
- raise MMPMalformedPacket,"Can't extract string from data"
- string_length = struct.unpack('I',data[:size_length])[0]
- data = data[size_length:]
- if len(data) < string_length:
- raise MMPMalformedPacket("Incorrect string length received (string length = %d data length = %d)"%(string_length,len(data)))
- string = struct.unpack("%ds"%string_length,data[:string_length])
- data = data[string_length:]
- return string
-
class MMPHeader(object):
size = 44 # usual MMP header size as stated in
# protocol specification
@@ -65,6 +53,74 @@ def binary_data(self):
self.dlen,
*([0]*18))
+class MMPGroup(object):
+ def __init__(self,flags,name):
+ self.flags = flags
+ self.name = name
+
+class MMPContact(object):
+ def __init__(self,flags,group,address,nickname,server_flags,status):
+ self.flags = flags
+ self.group = group
+ self.address = address
+ self.nickname = nickname
+ self.server_flags = server_flags
+ self.status = status
+
+class PackingMixin(object):
+ def unpack_lps(self):
+ size_length = struct.calcsize('I')
+ if len(self.binary_data) < size_length:
+ raise MMPMalformedPacket,"Can't extract string from binary_data"
+ string_length = struct.unpack('I',self.binary_data[:size_length])[0]
+ self.binary_data = self.binary_data[size_length:]
+ if len(self.binary_data) < string_length:
+ raise MMPMalformedPacket,"Incorrect string length received"
+ string = struct.unpack("%ds"%string_length,self.binary_data[:string_length])[0]
+ self.binary_data = self.binary_data[string_length:]
+ return string
+
+ def unpack_uint(self):
+ size = struct.calcsize('I')
+ if len(self.binary_data) < size:
+ raise MMPMalformedPacket,"Can't extract unsinged int, not enough binary_data"
+ result = struct.unpack('I',self.binary_data[:size])[0]
+ self.binary_data = self.binary_data[size:]
+ return result
+
+ def unpack_zstring(self):
+ ''' unpack zero-ended string '''
+ zero_index = self.binary_data.index('\0')
+ result = self.binary_data[:zero_index]
+ self.binary_data = self.binary_data[zero_index+1:]
+ return result
+
+ def unpack_with_mask(self,mask):
+ if mask:
+ print "mask = %s"%mask
+ result = []
+ for symbol in mask:
+ if symbol=='u': result.append(self.unpack_uint())
+ elif symbol=='s': result.append(self.unpack_lps())
+ elif symbol=='z': result.append(self.unpack_zstring())
+ else: raise MMPMalformedPacket,"Unknown mask"
+ return tuple(result)
+
+ def pack_lps(self,string):
+ return struct.pack('I',len(string)) + string
+
+ def pack_uint(self,value):
+ return struct.pack('I',value)
+
+class MMPClientPingPacket(object):
+ msg = MRIM_CS_PING
+ def __init__(self,header):
+ self.header = header
+ self.header.dlen = 0
+
+ def binary_data(self):
+ return self.header.binary_data()
+
class MMPClientHelloPacket(object):
msg = MRIM_CS_HELLO
def __init__(self,header):
@@ -74,38 +130,90 @@ def __init__(self,header):
def binary_data(self):
return self.header.binary_data()
-class MMPClientLogin2Packet(object):
+class MMPClientLogin2Packet(PackingMixin):
msg = MRIM_CS_LOGIN2
def __init__(self,header,email,password):
self.header = header
self.header.msg = self.__class__.msg
self.email = email
self.password = password
- self.header.dlen = struct.calcsize('4I')+len(email)+len(password)+len(MMP_CLIENT_STRING)
+ self.header.dlen = len(self.binary_data()) - MMPHeader.size
def binary_data(self):
- header_data = self.header.binary_data()
- payload = ""
- payload += struct.pack('I',len(self.email)) + self.email
- payload += struct.pack('I',len(self.password)) + self.password
- payload += struct.pack('I',STATUS_ONLINE)
- payload += struct.pack('I',len(MMP_CLIENT_STRING)) + MMP_CLIENT_STRING
- return header_data+payload
-
-class MMPServerHelloAckPacket(object):
+ data = self.header.binary_data()
+ data += self.pack_lps(self.email)
+ data += self.pack_lps(self.password)
+ data += self.pack_uint(STATUS_ONLINE)
+ data += self.pack_lps(MMP_CLIENT_STRING)
+ return data
+
+class MMPClientMessageRecvPacket(PackingMixin):
+ msg = MRIM_CS_MESSAGE_RECV
+ def __init__(self,header,from_email,msgid):
+ self.header = header
+ self.header.msg = self.__class__.msg
+ self.from_email = from_email
+ self.msgid = msgid
+ self.header.dlen = len(self.binary_data()) - MMPHeader.size
+ def binary_data(self):
+ data = self.header.binary_data()
+ data += self.pack_lps(self.from_email)
+ data += self.pack_uint(msgid)
+ return data
+
+class MMPServerHelloAckPacket(PackingMixin):
msg = MRIM_CS_HELLO_ACK
def __init__(self, header, binary_data):
self.header = header
- if len(binary_data) != struct.calcsize('I'):
- raise MMPMalformedPacket, "Wrong size of HelloAck payload"
- self.interval = struct.unpack('I',binary_data)
+ self.binary_data = binary_data
+ self.interval = self.unpack_uint()
-class MMPServerLoginAckPacket(object):
+class MMPServerLoginAckPacket(PackingMixin):
msg = MRIM_CS_LOGIN_ACK
def __init__(self, header, binary_data):
self.header = header
-class MMPServerLoginRejPacket(object):
+class MMPServerLoginRejPacket(PackingMixin):
msg = MRIM_CS_LOGIN_REJ
def __init__(self, header, binary_data):
self.header = header
+ self.binary_data = binary_data
self.reason = unpack_lps(binary_data)
+
+class MMPServerMessageAckPacket(PackingMixin):
+ msg = MRIM_CS_MESSAGE_ACK
+ def __init__(self, header, binary_data):
+ self.header = header
+ self.binary_data = binary_data
+ self.msgid = self.unpack_uint()
+ self.flags = self.unpack_uint()
+ self.from_email = self.unpack_lps()
+ self.message = self.unpack_lps()
+ if self.flag_set(MESSAGE_FLAG_RTF):
+ self.rtf_message = self.unpack_lps()
+ def flag_set(self,flag):
+ return (self.flags | flag) == self.flags
+
+class MMPServerContactListPacket(PackingMixin):
+ msg = MRIM_CS_CONTACT_LIST2
+ def __init__(self, header, binary_data):
+ self.header = header
+ self.binary_data = binary_data
+ self.status = self.unpack_uint()
+ self.groups_number = self.unpack_uint()
+ self.group_mask = self.unpack_lps()
+ self.contacts_mask = self.unpack_lps()
+
+ self.groups = []
+ self.contacts = []
+ for i in range(self.groups_number):
+ self.groups += [MMPGroup(self.unpack_uint(),self.unpack_lps())]
+ self.unpack_with_mask(self.group_mask[2:])
+ while len(self.binary_data) > 0:
+ flags = self.unpack_uint()
+ group = self.unpack_uint()
+ address = self.unpack_lps()
+ nick = self.unpack_lps()
+ server_flags = self.unpack_uint()
+ status = self.unpack_uint()
+ self.contacts += [MMPContact(flags,group,address,nick,server_flags,status)]
+ self.unpack_with_mask(self.contacts_mask[6:])
Oops, something went wrong.

0 comments on commit ec463f1

Please sign in to comment.