8c7aeb7423ee7265ffd2d8e0e53311961d85472a
[vuplus_dvbapp-plugin] / webcamviewer / src / FTPDownloader.py
1 from twisted.internet import reactor, defer
2 from twisted.internet.protocol import Protocol, ClientCreator
3 from twisted.protocols.ftp import FTPClient, FTPFileListProtocol
4
5 from os import SEEK_END
6
7 # XXX: did I ever actually test supportPartial?
8 class FTPDownloader(Protocol):
9         """Download to a file from FTP."""
10
11         def __init__(self, host, port, path, fileOrName, username = 'anonymous', \
12                 password = 'my@email.com', passive = True, supportPartial = False, \
13                 *args, **kwargs):
14
15                 timeout = 30
16
17                 # We need this later
18                 self.path = path
19                 self.resume = supportPartial
20
21                 # Output
22                 if isinstance(fileOrName, str):
23                         self.filename = fileOrName
24                         self.file = None
25                 else:
26                         self.file = fileOrName
27
28                 creator = ClientCreator(reactor, FTPClient, username, password, passive = passive)
29
30                 creator.connectTCP(host, port, timeout).addCallback(self.controlConnectionMade).addErrback(self.connectionFailed)
31
32                 self.deferred = defer.Deferred()
33
34         def controlConnectionMade(self, ftpclient):
35                 # We need the client locally
36                 self.ftpclient = ftpclient
37
38                 # Try to fetch filesize
39                 self.ftpFetchSize()
40
41         # Handle recieved msg
42         def sizeRcvd(self, msgs):
43                 # Split up return
44                 code, msg = msgs[0].split()
45                 if code == '213':
46                         self.totallength = int(msg)
47                         # We know the size, so start fetching
48                         self.ftpFetchFile()
49                 else:
50                         # Error while reading size, try to list it
51                         self.ftpFetchList()
52
53         def ftpFetchSize(self):
54                 d = self.ftpclient.queueStringCommand('SIZE ' + self.path)
55                 d.addCallback(self.sizeRcvd).addErrback(self.ftpFetchList)
56
57         # Handle recieved msg
58         def listRcvd(self, *args):
59                 # Quit if file not found
60                 if not len(self.filelist.files):
61                         self.connectionFailed()
62                         return
63
64                 self.totallength = self.filelist.files[0]['size']
65
66                 # Invalidate list
67                 self.filelist = None
68
69                 # We know the size, so start fetching
70                 self.ftpFetchFile()
71
72         def ftpFetchList(self, *args, **kwargs):
73                 self.filelist = FTPFileListProtocol()
74                 d = self.ftpclient.list(self.path, self.filelist)
75                 d.addCallback(self.listRcvd).addErrback(self.connectionFailed)
76
77         def openFile(self):
78                 if self.resume:
79                         file = open(self.filename, 'ab')
80                 else:
81                         file = open(self.filename, 'wb')
82
83                 return (file, file.tell())
84
85         def ftpFetchFile(self):
86                 offset = 0
87
88                 # Finally open file
89                 if self.file is None:
90                         try:
91                                 self.file, offset = self.openFile()
92                         except IOError, ie:
93                                 self.connectionFailed()
94                                 return
95
96                 offset = self.resume and offset or 0
97
98                 d = self.ftpclient.retrieveFile(self.path, self, offset = offset)
99                 d.addCallback(self.ftpFinish).addErrback(self.connectionFailed)
100
101         def dataReceived(self, data):
102                 if not self.file:
103                         return
104
105                 try:
106                         self.file.seek(0, SEEK_END)
107
108                         self.file.write(data)
109                 except IOError, ie:
110                         self.connectionFailed()
111
112         def ftpFinish(self, code = 0, message = None):
113                 self.ftpclient.quit()
114                 if self.file is not None:
115                         self.file.close()
116                 self.deferred.callback(code)
117
118         def connectionFailed(self, reason = None):
119                 if self.file is not None:
120                         self.file.close()
121                 self.deferred.errback(reason)