]> code.delx.au - pymsnt/blobdiff - src/tlib/msn/msnft.py
Added auto-reconnect.
[pymsnt] / src / tlib / msn / msnft.py
index 33e0034e935bc34930ed6415d80d24b79d4890a5..5bb0eaa65e1fd6cda6a275966545818934acb615 100644 (file)
@@ -20,47 +20,47 @@ MAXAUTHCOOKIE = 2**32-1
 class MSNFTReceive_Base:
        # Public
        def __init__(self, filename, filesize, userHandle):
-               self.connector = None
+               self.consumer = None
                self.finished = False
                self.error = False
                self.buffer = []
                self.filename, self.filesize, self.userHandle = filename, filesize, userHandle
        
        def removeMe(self):
-               self.connector = None
+               self.consumer = None
        
        def accept(self, yes=True):
                pass
        
        def writeTo(self, obj):
-               self.connector = obj
+               self.consumer = obj
                for data in self.buffer:
-                       self.connector.write(data)
+                       self.consumer.write(data)
                self.buffer = []
                if self.finished:
-                       self.connector.close()
+                       self.consumer.close()
                if self.error:
-                       self.connector.error()
+                       self.consumer.error()
 
 
        # Private
        def write(self, data):
-               if self.connector:
-                       self.connector.write(data)
+               if self.consumer:
+                       self.consumer.write(data)
                else:
                        self.buffer.append(data)
        
        def close(self):
                self.removeMe()
                self.finished = True
-               if self.connector:
-                       self.connector.close()
+               if self.consumer:
+                       self.consumer.close()
        
        def gotError(self, ignored=None):
                self.removeMe()
                self.error = True
-               if self.connector:
-                       self.connector.error()
+               if self.consumer:
+                       self.consumer.error()
 
 
 class MSNFTP_Ports:
@@ -304,3 +304,104 @@ class MSNFTP_FileReceive(LineReceiver):
                self.file.write(data)
 
 
+class MSNFTP_FileSend(LineReceiver):
+    """
+    This class provides support for sending files to other contacts.
+
+    @ivar bytesSent: the number of bytes that have currently been sent.
+    @ivar completed: true if the send has completed.
+    @ivar connected: true if a connection has been established.
+    @ivar targetUser: the target user (contact).
+    @ivar segmentSize: the segment (block) size.
+    @ivar auth: the auth cookie (number) to use when sending the
+                transfer invitation
+    """
+    
+    def __init__(self, file):
+        """
+        @param file: A string or file object represnting the file to send.
+        """
+
+        if isinstance(file, str) or isinstance(file, unicode):
+            self.file = open(file, 'rb')
+        else:
+            self.file = file
+
+        self.fileSize = 0
+        self.bytesSent = 0
+        self.completed = 0
+        self.connected = 0
+        self.targetUser = None
+        self.segmentSize = 2045
+        self.auth = random.randint(0, 2**30)
+        self._pendingSend = None # :(
+
+    def connectionMade(self):
+        self.connected = 1
+
+    def connectionLost(self, reason):
+        if self._pendingSend:
+            self._pendingSend.cancel()
+            self._pendingSend = None
+        self.connected = 0
+        self.file.close()
+
+    def lineReceived(self, line):
+        temp = line.split(' ')
+        if len(temp) == 1: params = []
+        else: params = temp[1:]
+        cmd = temp[0]
+        handler = getattr(self, "handle_%s" % cmd.upper(), None)
+        if handler: handler(params)
+        else: self.handle_UNKNOWN(cmd, params)
+
+    def handle_VER(self, params):
+        checkParamLen(len(params), 1, 'VER')
+        if params[0].upper() == "MSNFTP":
+            self.sendLine("VER MSNFTP")
+        else: # they sent some weird version during negotiation, i'm quitting.
+            self.transport.loseConnection()
+
+    def handle_USR(self, params):
+        checkParamLen(len(params), 2, 'USR')
+        self.targetUser = params[0]
+        if self.auth == int(params[1]):
+            self.sendLine("FIL %s" % (self.fileSize))
+        else: # they failed the auth test, disconnecting.
+            self.transport.loseConnection()
+
+    def handle_TFR(self, params):
+        checkParamLen(len(params), 0, 'TFR')
+        # they are ready for me to start sending
+        self.sendPart()
+
+    def handle_BYE(self, params):
+        self.completed = (self.bytesSent == self.fileSize)
+        self.transport.loseConnection()
+
+    def handle_CCL(self, params):
+        self.completed = (self.bytesSent == self.fileSize)
+        self.transport.loseConnection()
+
+    def handle_UNKNOWN(self, cmd, params): log.msg('received unknown command (%s), params: %s' % (cmd, params))
+
+    def makeHeader(self, size):
+        """ make the appropriate header given a specific segment size. """
+        quotient, remainder = divmod(size, 256)
+        return chr(0) + chr(remainder) + chr(quotient)
+
+    def sendPart(self):
+        """ send a segment of data """
+        if not self.connected:
+            self._pendingSend = None
+            return # may be buggy (if handle_CCL/BYE is called but self.connected is still 1)
+        data = self.file.read(self.segmentSize)
+        if data:
+            dataSize = len(data)
+            header = self.makeHeader(dataSize)
+            self.transport.write(header + data)
+            self.bytesSent += dataSize
+            self._pendingSend = reactor.callLater(0, self.sendPart)
+        else:
+            self._pendingSend = None
+            self.completed = 1