]> code.delx.au - pymsnt/blobdiff - src/legacy/msn/msn.py
Handle new MSN accounts correctly
[pymsnt] / src / legacy / msn / msn.py
index 2bc306c4f33a290f20572ce3508282b603f32bd5..ddc13d21781fad46df2a1fe01fc3937184f57f10 100644 (file)
@@ -84,6 +84,9 @@ Use of this module requires that PyOpenSSL is installed.
 
 from __future__ import nested_scopes
 
+import twistfix
+twistfix.main()
+
 # Sibling imports
 from twisted.protocols.basic import LineReceiver
 from twisted.web.http import HTTPClient
@@ -113,6 +116,7 @@ MSN_MAX_MESSAGE      = 1664               # max message length
 MSN_CVR_STR          = "0x040c winnt 5.1 i386 MSNMSGR 7.0.0777 msmsgs"
 MSN_AVATAR_GUID      = "{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}"
 MSN_MSNFTP_GUID      = "{5D3E02AB-6190-11D3-BBBB-00C04F795683}"
+MSN_MAXINT           = 2**31 - 1
 
 # auth constants
 LOGIN_SUCCESS  = 1
@@ -464,7 +468,7 @@ class MSNObject:
     Used to represent a MSNObject. This can be currently only be an avatar.
 
     @ivar creator: The userHandle of the creator of this picture.
-    @ivar imageData: The PNG image data (only for our own avatar)
+    @ivar imageDataFunc: A function to return the PNG image data (only for our own avatar)
     @ivar type: Always set to 3, for avatar.
     @ivar size: The size of the image.
     @ivar location: The filename of the image.
@@ -477,10 +481,11 @@ class MSNObject:
         if s:
             self.parse(s)
     
-    def setData(self, creator, imageData):
+    def setData(self, creator, imageDataFunc):
         """ Set the creator and imageData for this object """
+        imageData = imageDataFunc()
         self.creator = creator
-        self.imageData = imageData
+        self.imageDataFunc = imageDataFunc
         self.size = len(imageData)
         self.type = 3
         self.location = "TMP" + str(random.randint(1000,9999))
@@ -490,7 +495,7 @@ class MSNObject:
     
     def setNull(self):
         self.creator = ""
-        self.imageData = ""
+        self.imageDataFunc = lambda: None
         self.size = 0
         self.type = 0
         self.location = ""
@@ -520,13 +525,18 @@ class MSNObject:
         e = parseText(s, True)
         if not e:
             return # Parse failed
-        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
+        try:
+            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
+        except TypeError:
+            self.setNull()
+        except ValueError:
+            self.setNull()
 
 
 class MSNContact:
@@ -1211,38 +1221,37 @@ class NotificationClient(MSNEventBase):
             self._setStateData('lst_sofar',sofar)
 
     def handle_BLP(self, params):
-        # check to see if this is in response to a SYN
-        if self._getState() == 'SYNC':
-            self._getStateData('list').privacy = listCodeToID[params[0].lower()]
-        else:
+        # If this is in response to a SYN, then there will be a transaction ID
+        try:
             id = int(params[0])
             self.factory.contacts.privacy = listCodeToID[params[1].lower()]
             self._fireCallback(id, params[1])
+        except ValueError:
+            self._getStateData('list').privacy = listCodeToID[params[0].lower()]
 
     def handle_GTC(self, params):
-        # check to see if this is in response to a SYN
-        if self._getState() == 'SYNC':
-            if params[0].lower() == "a": self._getStateData('list').autoAdd = 0
-            elif params[0].lower() == "n": self._getStateData('list').autoAdd = 1
-            else: raise MSNProtocolError, "Invalid Paramater for GTC" # debug
-        else:
+        # If this is in response to a SYN, then there will be a transaction ID
+        try:
             id = int(params[0])
             if params[1].lower() == "a": self._fireCallback(id, 0)
             elif params[1].lower() == "n": self._fireCallback(id, 1)
             else: raise MSNProtocolError, "Invalid Paramater for GTC" # debug
+        except ValueError:
+            if params[0].lower() == "a": self._getStateData('list').autoAdd = 0
+            elif params[0].lower() == "n": self._getStateData('list').autoAdd = 1
+            else: raise MSNProtocolError, "Invalid Paramater for GTC" # debug
 
     def handle_SYN(self, params):
         id = int(params[0])
-        self._setStateData('phone', []) # Always needs to be set
-        if params[3] == 0: # No LST will be received. New account?
+        contacts = MSNContactList()
+        self._setStateData('list', contacts)
+        self._setStateData('phone', [])
+        self._setStateData('lst_reply', int(params[3]))
+        self._setStateData('lsg_reply', int(params[4]))
+        self._setStateData('lst_sofar', 0)
+        if params[3] == "0": # No LST will be received. New account?
             self._setState('SESSION')
-            self._fireCallback(id, None, None)
-        else:
-            contacts = MSNContactList()
-            self._setStateData('list', contacts)
-            self._setStateData('lst_reply', int(params[3]))
-            self._setStateData('lsg_reply', int(params[4]))
-            self._setStateData('lst_sofar', 0)
+            self._fireCallback(id, contacts, [])
 
     def handle_LSG(self, params):
         if self._getState() == 'SYNC':
@@ -1251,10 +1260,11 @@ class NotificationClient(MSNEventBase):
     def handle_PRP(self, params):
         if params[1] == "MFN":
             self._fireCallback(int(params[0]))
-        elif self._getState() == 'SYNC':
-            self._getStateData('phone').append((params[0], unquote(params[1])))
         else:
-            self._fireCallback(int(params[0]), int(params[1]), unquote(params[3]))
+            try:
+                self._fireCallback(int(params[0]), int(params[1]), unquote(params[3]))
+            except ValueError:
+                self._getStateData('phone').append((params[0], unquote(params[1])))
 
     def handle_BPR(self, params):
         numParams = len(params)
@@ -1937,11 +1947,11 @@ class NotificationClient(MSNEventBase):
             return (personal,)
         return d.addCallback(_cb)
 
-    def changeAvatar(self, imageData, push):
+    def changeAvatar(self, imageDataFunc, push):
         """
         Used to change the avatar that other users see.
 
-        @param imageData: the PNG image data to set as the avatar
+        @param imageDataFunc: a function to return the PNG image data to set as the avatar
         @param push     : whether to push the update to the server
                           (it will otherwise be sent with the next
                           changeStatus())
@@ -1951,12 +1961,18 @@ class NotificationClient(MSNEventBase):
                  The callback argument will be the same as for changeStatus.
         """
 
-        if self.msnobj and imageData == self.msnobj.imageData: return
-        if imageData:
-            self.msnobj.setData(self.factory.userHandle, imageData)
+        checkMsnobj = MSNObject()
+        checkMsnobj.setData(self.factory.userHandle, imageDataFunc)
+        if self.msnobj and self.msnobj.sha1d == checkMsnobj.sha1d:
+            return # Avatar hasn't changed
+        if imageDataFunc:
+            # We need to keep the same MSNObject instance, as it is
+            # passed on to SwitchboardClient objects
+            self.msnobj.setData(self.factory.userHandle, imageDataFunc)
         else:
             self.msnobj.setNull()
-        if push: return self.changeStatus(self.factory.status) # Push to server
+        if push:
+            return self.changeStatus(self.factory.status) # Push to server
 
 
     def requestSwitchboardServer(self):
@@ -2147,7 +2163,7 @@ class SwitchboardClient(MSNEventBase):
                     # Check that we have an avatar to send
                     if self.msnobj:
                         slpLink = SLPLink_AvatarSend(remoteUser=slpMessage.fro, switchboard=self, filesize=self.msnobj.size, sessionID=slpMessage.sessionID, sessionGuid=slpMessage.sessionGuid)
-                        slpLink.write(self.msnobj.imageData)
+                        slpLink.write(self.msnobj.imageDataFunc())
                         slpLink.close()
                     else:
                         # They shouldn't have sent a request if we have
@@ -2681,7 +2697,7 @@ class SeqID:
         if baseID:
             self.baseID = baseID
         else:
-            self.baseID = random.randint(1000, sys.maxint)
+            self.baseID = random.randint(1000, MSN_MAXINT)
         self.pos = -1
 
     def get(self):
@@ -2708,7 +2724,7 @@ class SLPLink:
     def __init__(self, remoteUser, switchboard, sessionID, sessionGuid):
         self.dataFlag = 0
         if not sessionID:
-            sessionID = random.randint(1000, sys.maxint)
+            sessionID = random.randint(1000, MSN_MAXINT)
         if not sessionGuid:
             sessionGuid = random_guid()
         self.remoteUser = remoteUser
@@ -2754,7 +2770,7 @@ class SLPLink:
         binaryFields[1] = self.seqID.next()
         binaryFields[3] = len(msgStr)
         binaryFields[4] = binaryFields[3]
-        binaryFields[6] = random.randint(1000, sys.maxint)
+        binaryFields[6] = random.randint(1000, MSN_MAXINT)
         self.sendP2PMessage(binaryFields, msgStr)
 
     def sendP2PMessage(self, binaryFields, msgStr):
@@ -2788,7 +2804,7 @@ class SLPLink_Send(SLPLink):
         binaryFields[1] = self.seqID.next()
         binaryFields[3] = 4
         binaryFields[4] = 4
-        binaryFields[6] = random.randint(1000, sys.maxint)
+        binaryFields[6] = random.randint(1000, MSN_MAXINT)
         binaryFields[9] = 1
         self.sendP2PMessage(binaryFields, chr(0) * 4)
 
@@ -2818,7 +2834,7 @@ class SLPLink_Send(SLPLink):
         binaryFields[3] = self.filesize
         binaryFields[4] = len(chunk)
         binaryFields[5] = self.dataFlag
-        binaryFields[6] = random.randint(1000, sys.maxint)
+        binaryFields[6] = random.randint(1000, MSN_MAXINT)
         binaryFields[9] = 1
         self.offset += len(chunk)
         self.sendP2PMessage(binaryFields, chunk)