]> code.delx.au - pymsnt/commitdiff
Reimport and tags (0.10.1)
authorjamesbunton <jamesbunton@55fbd22a-6204-0410-b2f0-b6c764c7e90a>
Mon, 31 Oct 2005 01:12:03 +0000 (01:12 +0000)
committerjamesbunton <jamesbunton@55fbd22a-6204-0410-b2f0-b6c764c7e90a>
Mon, 31 Oct 2005 01:12:03 +0000 (01:12 +0000)
git-svn-id: http://delx.cjb.net/svn/pymsnt/trunk@4 55fbd22a-6204-0410-b2f0-b6c764c7e90a

committer: jamesbunton <jamesbunton@55fbd22a-6204-0410-b2f0-b6c764c7e90a>

src/tlib/msnp11chl.py [new file with mode: 0644]
src/tlib/msnp2p.py [new file with mode: 0644]

diff --git a/src/tlib/msnp11chl.py b/src/tlib/msnp11chl.py
new file mode 100644 (file)
index 0000000..36964e5
--- /dev/null
@@ -0,0 +1,76 @@
+# Copyright 2005 James Bunton <james@delx.cjb.net>
+# Licensed for distribution under the GPL version 2, check COPYING for details
+
+import md5
+import struct
+import string
+
+MSNP11_PRODUCT_ID = "PROD0090YUAUV{2B"
+MSNP11_PRODUCT_KEY = "YMM8C_H7KCQ2S_KL"
+MSNP11_MAGIC_NUM = 0x0E79A9C1
+
+
+def doChallenge(chlData):
+       md5digest = md5.md5(chlData + MSNP11_PRODUCT_KEY).digest()
+
+       # Make array of md5 string ints
+       md5Ints = struct.unpack("<llll", md5digest)
+       md5Ints = [(x & 0x7fffffff) for x in md5Ints]
+
+       # Make array of chl string ints
+       chlData += MSNP11_PRODUCT_ID
+       amount = 8 - len(chlData) % 8
+       chlData += string.zfill("", amount)
+       chlInts = struct.unpack("<%di" % (len(chlData)/4), chlData)
+
+       # Make the key
+       high = 0
+       low = 0
+       i = 0
+       while i < len(chlInts) - 1:
+               temp = chlInts[i]
+               temp = (MSNP11_MAGIC_NUM * temp) % 0x7FFFFFFF
+               temp += high
+               temp = md5Ints[0] * temp + md5Ints[1]
+               temp = temp % 0x7FFFFFFF
+
+               high = chlInts[i + 1]
+               high = (high + temp) % 0x7FFFFFFF
+               high = md5Ints[2] * high + md5Ints[3]
+               high = high % 0x7FFFFFFF
+
+               low = low + high + temp
+
+               i += 2
+
+       high = littleEndify((high + md5Ints[1]) % 0x7FFFFFFF)
+       low = littleEndify((low + md5Ints[3]) % 0x7FFFFFFF)
+       key = (high << 32L) + low
+       key = littleEndify(key, "Q")
+
+       longs = [x for x in struct.unpack(">QQ", md5digest)]
+       longs = [littleEndify(x, "Q") for x in longs]
+       longs = [x ^ key for x in longs]
+       longs = [littleEndify(abs(x), "Q") for x in longs]
+       
+       out = ""
+       for x in longs:
+               x = hex(x)
+               x = x[2:len(x)-1]
+               x = x.zfill(16)
+               out += x.lower()
+       
+       return out
+
+def littleEndify(num, c="L"):
+       return struct.unpack(">" + c, struct.pack("<" + c, num))[0]
+
+
+if __name__ == "__main__":
+       import sys
+       try:
+               print doChallenge(sys.argv[1])
+       except IndexError:
+               print "No args passed"
+               raise
+
diff --git a/src/tlib/msnp2p.py b/src/tlib/msnp2p.py
new file mode 100644 (file)
index 0000000..ceaadfd
--- /dev/null
@@ -0,0 +1,700 @@
+# Copyright 2005 James Bunton <james@delx.cjb.net>
+# Licensed for distribution under the GPL version 2, check COPYING for details
+
+import struct
+import random
+import sha
+
+import utils
+
+MSNP2P_DEBUG = False
+
+class MSNOBJ:
+       def __init__(self):
+               self.creator = ""
+               self.imageData = ""
+               self.size = ""
+               self.type = 0
+               self.location = ""
+               self.friendly = ""
+               self.sha1d = ""
+               self.text = ""
+       
+       def setData(self, creator, imageData):
+               self.creator = creator
+               self.imageData = imageData
+               self.size = len(imageData)
+               self.type = 3
+               self.location = "TMP" + str(random.randint(1000,9999))
+               self.friendly = "AAA="
+               self.sha1d = utils.b64enc(sha.sha(imageData).digest())
+               self.makeText()
+       
+       def setNull(self):
+               self.creator = ""
+               self.imageData = ""
+               self.size = 0
+               self.type = 0
+               self.location = ""
+               self.friendly = ""
+               self.sha1d = ""
+               self.text = ""
+       
+       def makeText(self):
+               h = []
+               h.append("Creator")
+               h.append(self.creator)
+               h.append("Size")
+               h.append(str(self.size))
+               h.append("Type")
+               h.append(str(self.type))
+               h.append("Location")
+               h.append(self.location)
+               h.append("Friendly")
+               h.append(self.friendly)
+               h.append("SHA1D")
+               h.append(self.sha1d)
+               sha1c = utils.b64enc(sha.sha("".join(h)).digest())
+               self.text = '<msnobj Creator="%s" Size="%s" Type="%s" Location="%s" Friendly="%s" SHA1D="%s" SHA1C="%s"/>' % (self.creator, str(self.size), str(self.type), self.location, self.friendly, self.sha1d, sha1c)
+       
+       def parse(self, s):
+               e = utils.parseText(s)
+               self.creator = e.getAttribute("Creator")
+               self.size = int(e.getAttribute("Size"))
+               self.type = int(e.getAttribute("Type"))
+               self.location = e.getAttribute("Location")
+               self.friendly = e.getAttribute("Friendly")
+               self.sha1d = e.getAttribute("SHA1D")
+               self.text = s
+
+
+
+class BinaryFields:
+       ACK = 0x02
+       WAIT = 0x04
+       ERR = 0x08
+       DATA = 0x20
+       BYEGOT = 0x40
+       BYESENT = 0x80
+       DATAFT = 0x1000030
+
+       def __init__(self, fields=None):
+               if(not fields):
+                       fields = [0] * 10
+               self.fields = fields
+       
+       def __getitem__(self, key):
+               return self.fields[key]
+       
+       def __setitem__(self, key, value):
+               self.fields[key] = value
+       
+       def unpackFields(self, packet):
+               self.fields = struct.unpack("<LLQQLLLLQ", packet[0:48])
+               self.fields += struct.unpack(">L", packet[len(packet)-4:])
+               if MSNP2P_DEBUG:
+                       print "Unpacked fields:",
+                       for i in self.fields:
+                               print hex(i),
+                       print
+       
+       def packHeaders(self):
+               f = tuple(self.fields)
+               if MSNP2P_DEBUG:
+                       print "Packed fields:",
+                       for i in self.fields:
+                               print hex(i),
+                       print
+               return struct.pack("<LLQQLLLLQ", f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8])
+       
+       def packFooter(self):
+               return struct.pack(">L", self.fields[9])
+
+
+class MSNSLPMessage:
+       def __init__(self):
+               self.method = ""
+               self.status = ""
+               self.to = ""
+               self.fro = ""
+               self.cseq = 0
+               self.sessionGuid = ""
+               self.sessionID = None
+               self.euf_guid = ""
+               self.data = "\r\n" + chr(0)
+
+       def create(self, method=None, status=None, to=None, fro=None, cseq=0, sessionGuid=None, data=None):
+               self.method = method
+               self.status = status
+               self.to = to
+               self.fro = fro
+               self.cseq = cseq
+               self.sessionGuid = sessionGuid
+               if(data): self.data = data
+       
+       def setData(self, sessionID=None, appID=None, guid=None, context=None):
+               s = []
+               if(guid):       s.append("EUF-GUID: %s\r\n" % guid)
+               if(sessionID):  s.append("SessionID: %s\r\n" % sessionID)
+               if(appID):      s.append("AppID: %s\r\n" % appID)
+               if(context):    s.append("Context: %s\r\n\r\n" % context)
+               s.append(chr(0))
+               
+               self.data = "".join(s)
+       
+       def parse(self, s):
+               s = s[48:len(s)-4:]
+               if s.find("MSNSLP/1.0") < 0: return
+
+               lines = s.split("\r\n")
+               
+               # Get the MSNSLP method or status
+               msnslp = lines[0].split(" ")
+               if MSNP2P_DEBUG: print "Parsing MSNSLPMessage", s, len(s)
+               if(msnslp[0] in ("INVITE", "BYE")):
+                       self.method = msnslp[0].strip()
+               else:
+                       self.status = msnslp[1].strip()
+
+               lines.remove(lines[0])
+               
+               for line in lines:
+                       line = line.split(":")
+                       if(len(line) > 1):
+                               if(len(line) > 2 and line[0] == "To"):
+                                       self.to = line[2][:line[2].find('>')]
+                               elif(len(line) > 2 and line[0] == "From"):
+                                       self.fro = line[2][:line[2].find('>')]
+                               elif(line[0] == "Call-ID"):
+                                       self.sessionGuid = line[1].strip()
+                               elif(line[0] == "CSeq"):
+                                       self.cseq = int(line[1].strip())
+                               elif(line[0] == "SessionID"):
+                                       self.sessionID = int(line[1].strip())
+                               elif(line[0] == "EUF-GUID"):
+                                       self.euf_guid = line[1].strip()
+
+       def __str__(self):
+               s = []
+               if(self.method):
+                       s.append("%s MSNMSGR:%s MSNSLP/1.0\r\n" % (self.method, self.to))
+               else:
+                       s.append("MSNSLP/1.0 %s\r\n" % self.status)
+               s.append("To: <msnmsgr:%s>\r\n" % self.to)
+               s.append("From: <msnmsgr:%s>\r\n" % self.fro)
+               s.append("Via: MSNSLP/1.0/TLP ;branch=%s\r\n" % utils.random_guid())
+               s.append("CSeq: %s \r\n" % str(self.cseq))
+               s.append("Call-ID: %s\r\n" % self.sessionGuid)
+               s.append("Max-Forwards: 0\r\n")
+               if(self.method == "BYE"):
+                       s.append("Content-Type: application/x-msnmsgr-sessionclosebody\r\n")
+               else:
+                       s.append("Content-Type: application/x-msnmsgr-sessionreqbody\r\n")
+               s.append("Content-Length: %s\r\n\r\n" % len(self.data))
+               s.append(self.data)
+               return "".join(s)
+
+
+class BaseID:
+       def __init__(self, baseID=None, sender=False):
+               if(baseID):
+                       self.baseID = baseID
+               else:
+                       self.baseID = random.randint(4, 2**30)
+               self.sender = sender
+               if(self.sender):
+                       self.pos = -1
+               else:
+                       self.pos = 0
+       
+       def get(self):
+               if self.pos == -1:
+                       return self.baseID
+               else:
+                       return self.baseID + self.pos - 3
+       
+       def next(self):
+               self.pos += 1
+               if self.pos == 3 and self.sender:
+                       self.pos += 1
+               return self.get()
+
+class FileData:
+       def __init__(self, size):
+               self.data = chr(0) * size
+               self.totalSize = size
+               self.gotSize = 0
+       
+       def put(self, offset, data):
+               self.data = self.data[0:offset] + data + self.data[offset+len(data):]
+               self.gotSize += len(data)
+       
+       def finished(self):
+               return self.gotSize >= self.totalSize
+       
+
+class MSNP2P_Avatar:
+       TRANSFER_COUNT = 0
+       ERROR_COUNT = 0
+
+       EUF_GUID = "{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}"
+       ERROR = -1
+
+       def __init__(self):
+               raise "NotImplemented"
+
+       def getImage(self):
+               return False
+       
+       def isFinished(self):
+               if self.state >= self.FINISHED:
+                       MSNP2P_Avatar.TRANSFER_COUNT += 1
+                       return True
+               elif self.state < 0:
+                       MSNP2P_Avatar.ERROR_COUNT += 1
+                       return True
+               else:
+                       return False
+       
+       def setError(self, text):
+               if MSNP2P_DEBUG:
+                       print "ERROR in avatar transfer: ", self, text, "in state:", self.state
+               self.state = self.ERROR
+       
+       def warn(self, text):
+               if MSNP2P_DEBUG:
+                       print "Warning in avatar transfer: ", self, text, "in state:", self.state
+       
+
+class MSNP2P_Avatar_Send(MSNP2P_Avatar):
+       # Avatar_Send states
+       WAIT_REQUEST            = 0
+       SEND_REQACK             = 1
+       SEND_200OK              = 2
+       WAIT_200OK_ACK          = 3
+       SEND_DATAPREP           = 4
+       WAIT_DATAPREP_ACK       = 5
+       SEND_DATA               = 6
+       WAIT_BYE                = 7
+       SEND_BYE_ACK            = 8
+       FINISHED                = 9
+
+       def __init__(self, to, fro, msnobj):
+               self.to = to
+               self.fro = fro
+               self.msnobj = msnobj
+               self.fileOffset = 0 # The amount already sent
+               self.sessionID = 0
+               self.baseID = BaseID(sender=True)
+               self.state = self.WAIT_REQUEST
+               self.lastFields = BinaryFields()
+               self.lastSentFields = BinaryFields()
+       
+       def processPacket(self, packet):
+               if MSNP2P_DEBUG:
+                       print "processPacket", self.to, self.fro, self.state
+               if(self.state == self.WAIT_REQUEST):
+                       message = MSNSLPMessage()
+                       message.parse(packet)
+                       if(message.method != "INVITE"):
+                               self.setError("method" + message.method)
+                               return
+                       if(message.cseq != 0):
+                               self.setError("cseq" + str(message.cseq))
+                               return
+                       if(message.euf_guid != self.EUF_GUID):
+                               self.setError("guid" + message.euf_guid)
+                               return
+
+                       binaryFields = BinaryFields()
+                       binaryFields.unpackFields(packet)
+               
+                       self.sguid = message.sessionGuid
+                       self.sessionID = message.sessionID
+
+                       self.lastFields = binaryFields
+                       self.state = self.SEND_REQACK
+                       return
+
+               if(self.state == self.WAIT_200OK_ACK):
+                       binaryFields = BinaryFields()
+                       binaryFields.unpackFields(packet)
+
+                       if(binaryFields[6] != self.lastSentFields[1]):
+                               self.warn("field6," + str(binaryFields[6]) + "," + str(self.lastSentFields[1]))
+                               return
+                       if(binaryFields[3] != self.lastSentFields[3]):
+                               self.setError("field3," + str(binaryFields[3]) + "," + str(self.lastSentFields[3]))
+                               return
+                       if(binaryFields[8] != self.lastSentFields[3]):
+                               self.setError("field8," + str(binaryFields[8]) + "," + str(self.lastSentFields[3]))
+                               return
+                       if(binaryFields[5] != BinaryFields.ACK):
+                               self.setError("field5," + str(binaryFields[5]))
+                               return
+
+                       self.lastFields = binaryFields
+                       self.state = self.SEND_DATAPREP
+                       return
+
+               if(self.state == self.WAIT_DATAPREP_ACK):
+                       binaryFields = BinaryFields()
+                       binaryFields.unpackFields(packet)
+
+                       if(binaryFields[0] != self.sessionID):
+                               self.warn("field0," + str(binaryFields[0]) + "," + str(self.sessionID))
+                               return
+                       if(binaryFields[3] != self.lastSentFields[3]):
+                               self.setError("field3," + str(binaryFields[3]) + "," + str(self.lastSentFields[3]))
+                               return
+                       if(binaryFields[8] != self.lastSentFields[3]):
+                               self.setError("field8," + str(binaryFields[8]) + "," + str(self.lastSentFields[3]))
+                               return
+                       if(binaryFields[5] != BinaryFields.ACK):
+                               self.setError("field5," + str(binaryFields[5]))
+                               return
+                       if(binaryFields[6] != self.lastSentFields[1]):
+                               self.setError("field6," + str(binaryFields[6]) + "," + str(self.lastSentFields[1]))
+                               return
+                       if(binaryFields[7] != self.lastSentFields[6]):
+                               self.setError("field7," + str(binaryFields[7]) + "," + str(self.lastSentFields[6]))
+                               return
+
+                       self.lastFields = binaryFields
+                       self.baseID.next()
+                       self.state = self.SEND_DATA
+                       return
+
+               if(self.state == self.WAIT_BYE):
+                       binaryFields = BinaryFields()
+                       binaryFields.unpackFields(packet)
+                       
+                       # BYE message
+                       message = MSNSLPMessage()
+                       message.parse(packet)
+                       if(message.sessionGuid != self.sguid):
+                               self.warn("sessionGuid" + message.sessionGuid)
+                               return
+                       if(message.method != "BYE"):
+                               self.setError("method " + message.method)
+                               return
+                       if(message.cseq != 0):
+                               self.setError("cseq" + str(message.cseq))
+                               return
+       
+                       self.lastFields = binaryFields
+                       self.state = self.SEND_BYE_ACK # I don't really care about receiving the data ack
+                       return
+       
+
+       def getNextPacket(self):
+               if MSNP2P_DEBUG:
+                       print "getNextPacket (Avatar_Send)", self.to, self.fro, self.state
+               if(self.state == self.SEND_REQACK):
+                       binaryFields = BinaryFields()
+                       binaryFields[1] = self.baseID.get()
+                       binaryFields[3] = self.lastFields[3]
+                       binaryFields[5] = BinaryFields.ACK
+                       binaryFields[6] = self.lastFields[1]
+                       binaryFields[7] = self.lastFields[6]
+                       binaryFields[8] = self.lastFields[3]
+
+                       packet = binaryFields.packHeaders() + binaryFields.packFooter()
+
+                       self.state = self.SEND_200OK
+                       self.lastSentFields = binaryFields
+                       return packet
+
+               if(self.state == self.SEND_200OK):
+                       msg = MSNSLPMessage()
+                       msg.create(status="200 OK", to=self.to, fro=self.fro, cseq=1, sessionGuid=self.sguid)
+                       msg.setData(sessionID=self.sessionID)
+                       msgStr = str(msg)
+
+                       binaryFields = BinaryFields()
+                       binaryFields[1] = self.baseID.next()
+                       binaryFields[3] = len(msgStr)
+                       binaryFields[4] = len(msgStr)
+                       binaryFields[6] = random.randint(0, 2**30)
+
+                       packet = binaryFields.packHeaders() + msgStr + binaryFields.packFooter()
+                       
+                       self.state = self.WAIT_200OK_ACK
+                       self.lastSentFields = binaryFields
+                       return packet
+
+               if(self.state == self.SEND_DATAPREP):
+                       binaryFields = BinaryFields()
+                       binaryFields[0] = self.sessionID
+                       binaryFields[1] = self.baseID.next()
+                       binaryFields[3] = 4
+                       binaryFields[4] = 4
+                       binaryFields[6] = random.randint(0, 2**30)
+                       binaryFields[9] = 1
+
+                       packet = binaryFields.packHeaders() + chr(0) * 4 + binaryFields.packFooter()
+
+                       self.state = self.WAIT_DATAPREP_ACK
+                       self.lastSentFields = binaryFields
+                       return packet
+
+               if(self.state == self.SEND_DATA):
+                       binaryFields = BinaryFields()
+                       binaryFields[0] = self.sessionID
+                       binaryFields[1] = self.baseID.get()
+                       binaryFields[2] = self.fileOffset
+                       binaryFields[3] = len(self.msnobj.imageData)
+                       binaryFields[5] = BinaryFields.DATA
+                       binaryFields[6] = random.randint(0, 2**30)
+                       binaryFields[9] = 1
+
+                       # Work out what part of the file we're sending
+                       length = len(self.msnobj.imageData) - self.fileOffset
+                       if(length > 1202):
+                               # Max amount per message is 1202
+                               length = 1202
+                       else:
+                               # Last packet!
+                               self.state = self.WAIT_BYE
+                               self.lastSentFields = binaryFields
+                       binaryFields[4] = length
+                       chunk = self.msnobj.imageData[self.fileOffset : self.fileOffset + length]
+                       self.fileOffset += length
+
+                       packet = binaryFields.packHeaders() + chunk + binaryFields.packFooter()
+                       return packet
+
+               if(self.state == self.SEND_BYE_ACK):
+                       binaryFields = BinaryFields()
+                       binaryFields[1] = self.baseID.next()
+                       binaryFields[3] = self.lastFields[3]
+                       binaryFields[5] = BinaryFields.BYEGOT
+                       binaryFields[6] = self.lastFields[1]
+                       binaryFields[7] = self.lastFields[6]
+                       binaryFields[8] = self.lastFields[3]
+
+                       packet = binaryFields.packHeaders() + binaryFields.packFooter()
+
+                       self.state = self.FINISHED
+                       self.lastSentFields = binaryFields
+                       return packet
+
+
+
+
+class MSNP2P_Avatar_Receive(MSNP2P_Avatar):
+       # Avatar_Receive states
+       SEND_REQUEST            = 0     # Send the request
+       WAIT_REQACK_200OK       = 1     # Wait for REQACK and 200OK message
+       # Skip one value so we know both of the above have been done
+       SEND_200OK_ACK          = 3     # Received both of the above
+       WAIT_DATAPREP           = 4     # Wait for DATAPREP
+       SEND_DATAPREP_ACK       = 5     # Send ACK to above
+       WAIT_DATA               = 6     # Wait for all data messages and send DATA_ACK
+       SEND_DATA_ACK           = 7     # Then send BYE
+       SEND_BYE                = 8
+       FINISHED                = 9
+       
+       def __init__(self, to, fro, msnobj):
+               self.to = to
+               self.fro = fro
+               self.msnobj = msnobj
+               self.sessionID = random.randint(0, 2**30)
+               self.sguid = utils.random_guid()
+               self.baseID = BaseID()
+               self.state = self.SEND_REQUEST
+               self.lastFields = BinaryFields()
+               self.lastSentFields = BinaryFields()
+               self.fileData = None
+       
+       def getImage(self):
+               if(self.state >= self.SEND_BYE):
+                       return self.fileData.data
+       
+       def processPacket(self, packet):
+               if MSNP2P_DEBUG:
+                       print "processPacket", self.to, self.fro, self.state
+               if(self.state == self.WAIT_REQACK_200OK or self.state == self.WAIT_REQACK_200OK + 1):
+                       binaryFields = BinaryFields()
+                       binaryFields.unpackFields(packet)
+                       
+                       if(binaryFields[5] == BinaryFields.ACK):
+                               # REQUESTACK!
+                               if(binaryFields[6] != self.lastSentFields[1]):
+                                       self.warn("field6," + str(binaryFields[6]) + "," + str(self.lastSentFields[1]))
+                                       return
+                               if(binaryFields[3] != self.lastSentFields[3]):
+                                       self.setError("field3," + str(binaryFields[3]) + "," + str(self.lastSentFields[3]))
+                                       return
+                               if(binaryFields[7] != self.lastSentFields[6]):
+                                       self.setError("field7," + str(binaryFields[7]) + "," + str(self.lastSentFields[6]))
+                                       return
+                               if(binaryFields[8] != self.lastSentFields[3]):
+                                       self.setError("field8," + str(binaryFields[8]) + "," + str(self.lastSentFields[3]))
+                                       return
+
+                               self.state += 1
+                               return
+
+                       elif(binaryFields[5] == 0):
+                               # 200OK
+                               message = MSNSLPMessage()
+                               message.parse(packet)
+                               if(message.sessionID != self.sessionID):
+                                       self.warn("sessionID" + str(message.sessionID) + "," + str(self.sessionID))
+                                       return
+                               if(message.status != "200"):
+                                       self.setError("status" + message.status)
+                                       return
+                               if(message.cseq != 1):
+                                       self.setError("cseq" + str(message.cseq))
+                                       return
+                               if(message.sessionGuid != self.sguid):
+                                       self.setError("sessionGuid" + message.sessionGuid + "," + self.sguid)
+                                       return
+
+                               self.lastFields = binaryFields
+                               self.state += 1
+                               return
+
+                       else:
+                               self.warn("Packet discarded," + str(binaryFields[5]))
+               
+               if(self.state == self.WAIT_DATAPREP):
+                       binaryFields = BinaryFields()
+                       binaryFields.unpackFields(packet)
+
+                       if(binaryFields[0] != self.sessionID):
+                               self.warn("field0," + str(binaryFields[0]) + "," + str(self.sessionID))
+                               return
+                       if(binaryFields[3] != 4):
+                               self.setError("field3," + str(binaryFields[3]))
+                               return
+                       if(binaryFields[4] != 4):
+                               self.setError("field4," + str(binaryFields[4]))
+                               return
+                       if(binaryFields[9] != 1):
+                               self.warn("field9," + str(binaryFields[9]))
+                       #       return
+
+                       self.lastFields = binaryFields
+                       self.state = self.SEND_DATAPREP_ACK
+                       return
+
+               if(self.state == self.WAIT_DATA):
+                       binaryFields = BinaryFields()
+                       binaryFields.unpackFields(packet)
+                       if(binaryFields[0] != self.sessionID):
+                               self.warn("field0," + str(binaryFields[0]) + "," + str(self.sessionID))
+                               return
+                       if(binaryFields[5] != BinaryFields.DATA):
+                               self.setError("field5," + str(binaryFields[5]))
+                               return
+                       if(binaryFields[9] != 1):
+                               self.warn("field9," + str(binaryFields[9]))
+                       #       return
+                       offset = binaryFields[2]
+                       total = binaryFields[3]
+                       length = binaryFields[4]
+                       if(not self.fileData):
+                               self.fileData = FileData(binaryFields[3])
+
+                       data = packet[48:len(packet)-4]
+                       self.fileData.put(offset, packet[48:len(packet)-4])
+
+                       if self.fileData.finished():
+                               self.state = self.SEND_DATA_ACK
+
+                       self.lastFields = binaryFields
+
+                       return
+
+       def getNextPacket(self):
+               if MSNP2P_DEBUG:
+                       print "getNextPacket (Avatar_Receive)", self.to, self.fro, self.state
+               if(self.state == self.SEND_REQUEST):
+                       msg = MSNSLPMessage()
+                       msg.create(method="INVITE", to=self.to, fro=self.fro, cseq=0, sessionGuid=self.sguid)
+                       msg.setData(sessionID=self.sessionID, appID="1", guid=self.EUF_GUID, context=utils.b64enc(self.msnobj.text + chr(0)))
+                       msgStr = str(msg)
+
+                       binaryFields = BinaryFields()
+                       binaryFields[1] = self.baseID.get()
+                       binaryFields[6] = random.randint(0, 2**30)
+                       binaryFields[3] = len(msgStr)
+                       binaryFields[4] = len(msgStr)
+
+                       packet = binaryFields.packHeaders() + msgStr + binaryFields.packFooter()
+                       
+                       self.state = self.WAIT_REQACK_200OK
+                       self.lastSentFields = binaryFields
+                       return packet
+               
+               if(self.state == self.SEND_200OK_ACK):
+                       binaryFields = BinaryFields()
+                       binaryFields[1] = self.baseID.next()
+                       binaryFields[3] = self.lastFields[3]
+                       binaryFields[8] = self.lastFields[3]
+                       binaryFields[5] = BinaryFields.ACK
+                       binaryFields[6] = self.lastFields[1]
+                       binaryFields[7] = self.lastFields[6]
+
+                       packet = binaryFields.packHeaders() + binaryFields.packFooter()
+
+                       self.state = self.WAIT_DATAPREP
+                       self.lastSentFields = binaryFields
+                       return packet
+
+               if(self.state == self.SEND_DATAPREP_ACK):
+                       binaryFields = BinaryFields()
+                       binaryFields[0] = self.sessionID
+                       binaryFields[1] = self.baseID.next()
+                       binaryFields[3] = 4
+                       binaryFields[8] = 4
+                       binaryFields[5] = BinaryFields.ACK
+                       binaryFields[6] = self.lastFields[1]
+                       binaryFields[7] = self.lastFields[6]
+
+                       packet = binaryFields.packHeaders() + binaryFields.packFooter()
+
+                       self.state = self.WAIT_DATA
+                       self.lastSentFields = binaryFields
+                       return packet
+
+               if(self.state == self.SEND_DATA_ACK):
+                       # FIXME Check hash!
+                       binaryFields = BinaryFields()
+                       binaryFields[0] = self.sessionID
+                       binaryFields[1] = self.baseID.next()
+                       binaryFields[3] = self.lastFields[3]
+                       binaryFields[8] = self.lastFields[3]
+                       binaryFields[5] = BinaryFields.ACK
+                       binaryFields[6] = self.lastFields[1]
+                       binaryFields[7] = self.lastFields[6]
+
+                       packet = binaryFields.packHeaders() + binaryFields.packFooter()
+
+                       self.state = self.SEND_BYE
+                       self.lastSentFields = binaryFields
+                       return packet
+
+               if(self.state == self.SEND_BYE):
+                       msg = MSNSLPMessage()
+                       msg.create(method="BYE", to=self.to, fro=self.fro, cseq=0, sessionGuid=self.sguid)
+                       msgStr = str(msg)
+
+                       binaryFields = BinaryFields()
+                       binaryFields[1] = self.baseID.next()
+                       binaryFields[3] = len(msgStr)
+                       binaryFields[4] = len(msgStr)
+                       binaryFields[5] = BinaryFields.BYESENT
+                       binaryFields[6] = random.randint(0, 2**30)
+
+                       packet = binaryFields.packHeaders() + msgStr + binaryFields.packFooter()
+                       
+                       self.state = self.FINISHED
+                       self.lastSentFields = binaryFields
+                       return packet
+
+
+