E5Network/E5Ftp.py

changeset 2074
5cb87968aad5
child 2077
68a34718a0ce
equal deleted inserted replaced
2071:136eb25e4314 2074:5cb87968aad5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2012 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing an extension to the Python FTP class to support FTP proxies.
8 """
9
10 import ftplib
11 from socket import _GLOBAL_DEFAULT_TIMEOUT
12
13
14 class E5FtpProxyError(ftplib.Error):
15 """
16 Class to signal an error related to proxy configuration.
17
18 The error message starts with a three digit error code followed by a
19 space and the error string. Supported error codes are:
20 <ul>
21 <li>910: proxy error; the second number gives the category of the proxy error.
22 The original response from the proxy is appended in the next line.</li>
23 <li>930: proxy error; the second number gives the category of the proxy error.
24 The original response from the proxy is appended in the next line.</li>
25 <li>940: proxy error; the second number gives the category of the proxy error.
26 The original response from the proxy is appended in the next line.</li>
27 <li>950: proxy error; the second number gives the category of the proxy error.
28 The original response from the proxy is appended in the next line.</li>
29 <li>990: proxy usage is enabled but no proxy host given</li>
30 <li>991: proxy usage is enabled but no proxy user given</li>
31 <li>992: proxy usage is enabled but no proxy password given</li>
32 </ul>
33 """
34 pass
35
36
37 class E5FtpProxyType(object):
38 """
39 Class defining the supported FTP proxy types.
40 """
41 NoProxy = 0 # no proxy
42 NonAuthorizing = 1 # non authorizing proxy
43 UserAtServer = 2 # proxy login first, than user@remote.host
44 Site = 3 # proxy login first, than use SITE command
45 Open = 4 # proxy login first, than use OPEN command
46 UserAtProxyuserAtServer = 5 # one login for both
47 ProxyuserAtServer = 6 # proxy login with remote host given, than
48 # normal remote login
49 AuthResp = 7 # authenticate to proxy with AUTH and RESP commands
50 Bluecoat = 8 # bluecoat proxy
51
52
53 class E5Ftp(ftplib.FTP):
54 """
55 Class implementing an extension to the Python FTP class to support FTP proxies.
56 """
57 def __init__(self, host="", user="", password="", acct="",
58 proxyType=E5FtpProxyType.NoProxy, proxyHost="", proxyPort=ftplib.FTP_PORT,
59 proxyUser="", proxyPassword="", proxyAccount="",
60 timeout=_GLOBAL_DEFAULT_TIMEOUT):
61 """
62 Constructor
63
64 @param host name of the FTP host (string)
65 @param user user name for login to FTP host (string)
66 @param password password for login to FTP host (string)
67 @param acct account for login to FTP host (string)
68 @param proxyType type of the FTP proxy (integer 0 to 8)
69 @param proxyHost name of the FTP proxy (string)
70 @param proxyPort port of the FTP proxy (integer)
71 @param proxyUser user name for login to the proxy (string)
72 @param proxyPassword password for login to the proxy (string)
73 @param proxyAccount accounting info for the proxy (string)
74 @param timeout timeout in seconds for blocking operations (integer)
75 """
76 self.__timeout = timeout
77
78 self.__proxyType = proxyType
79 self.__proxyHost = proxyHost
80 self.__proxyPort = proxyPort
81 self.__proxyUser = proxyUser
82 self.__proxyPassword = proxyPassword
83 self.__proxyAccount = proxyAccount
84
85 self.__host = host
86 self.__port = ftplib.FTP_PORT
87 self.__user = user
88 self.__password = password
89 self.__acct = acct
90
91 if host:
92 self.connect(host)
93 if user:
94 self.login(user, password, acct)
95
96 def setProxy(self, proxyType=E5FtpProxyType.NoProxy, proxyHost="",
97 proxyPort=ftplib.FTP_PORT, proxyUser="", proxyPassword="", proxyAccount=""):
98 """
99 Public method to set the proxy configuration.
100
101 @param proxyType type of the FTP proxy (integer 0 to 8)
102 @param proxyHost name of the FTP proxy (string)
103 @param proxyPort port of the FTP proxy (integer)
104 @param proxyUser user name for login to the proxy (string)
105 @param proxyPassword password for login to the proxy (string)
106 @param proxyAccount accounting info for the proxy (string)
107 """
108 self.__proxyType = proxyType
109 self.__proxyHost = proxyHost
110 self.__proxyPort = proxyPort
111 self.__proxyUser = proxyUser
112 self.__proxyPassword = proxyPassword
113 self.__proxyAccount = proxyAccount
114
115 def setProxyAuthentication(self, proxyUser="", proxyPassword="", proxyAccount=""):
116 """
117 Public method to set the proxy authentication info.
118
119 @param proxyUser user name for login to the proxy (string)
120 @param proxyPassword password for login to the proxy (string)
121 @param proxyAccount accounting info for the proxy (string)
122 """
123 self.__proxyUser = proxyUser
124 self.__proxyPassword = proxyPassword
125 self.__proxyAccount = proxyAccount
126
127 def connect(self, host="", port=0, timeout=-999):
128 """
129 Public method to connect to the given FTP server.
130
131 This extended method connects to the proxy instead of the given host,
132 if a proxy is to be used. It throws an exception, if the proxy data
133 is incomplete.
134
135 @param host name of the FTP host (string)
136 @param port port of the FTP host (integer)
137 @param timeout timeout in seconds for blocking operations (integer)
138 @return welcome message of the server (string)
139 @exception E5FtpProxyError raised to indicate a proxy related issue
140 """
141 if host:
142 self.__host = host
143 if port:
144 self.__port = port
145 if timeout != -999:
146 self.__timeout = timeout
147
148 if self.__proxyType != E5FtpProxyType.NoProxy:
149 if not self.__proxyHost:
150 raise E5FtpProxyError(
151 "990 Proxy usage requested, but no proxy host given.")
152
153 return super().connect(self.__proxyHost, self.__proxyPort, self.__timeout)
154 else:
155 return super().connect(self.__host, self.__port, self.__timeout)
156
157 def login(self, user="", password="", acct=""):
158 """
159 Public method to login to the FTP server.
160
161 This extended method respects the FTP proxy configuration. There are many
162 different FTP proxy products available. But unfortunately there is no
163 standard for how o traverse a FTP proxy. The lis below shows the sequence
164 of commands used.
165
166 <table>
167 <tr><td>user</td><td>Username for remote host</td></tr>
168 <tr><td>pass</td><td>Password for remote host</td></tr>
169 <tr><td>pruser</td><td>Username for FTP proxy</td></tr>
170 <tr><td>prpass</td><td>Password for FTP proxy</td></tr>
171 <tr><td>remote.host</td><td>Hostname of the remote FTP server</td></tr>
172 </table>
173
174 <dl>
175 <dt>E5FtpProxyType.NoProxy</dt>
176 <dd>
177 USER user<br/>
178 PASS pass
179 </dd>
180 <dt>E5FtpProxyType.NonAuthorizing</dt>
181 <dd>
182 USER user@remote.host<br/>
183 PASS pass
184 </dd>
185 <dt>E5FtpProxyType.UserAtServer</dt>
186 <dd>
187 USER pruser<br/>
188 PASS prpass<br/>
189 USER user@remote.host<br/>
190 PASS pass
191 </dd>
192 <dt>E5FtpProxyType.Site</dt>
193 <dd>
194 USER pruser<br/>
195 PASS prpass<br/>
196 SITE remote.site<br/>
197 USER user<br/>
198 PASS pass
199 </dd>
200 <dt>E5FtpProxyType.Open</dt>
201 <dd>
202 USER pruser<br/>
203 PASS prpass<br/>
204 OPEN remote.site<br/>
205 USER user<br/>
206 PASS pass
207 </dd>
208 <dt>E5FtpProxyType.UserAtProxyuserAtServer</dt>
209 <dd>
210 USER user@pruser@remote.host<br/>
211 PASS pass@prpass
212 </dd>
213 <dt>E5FtpProxyType.ProxyuserAtServer</dt>
214 <dd>
215 USER pruser@remote.host<br/>
216 PASS prpass<br/>
217 USER user<br/>
218 PASS pass
219 </dd>
220 <dt>E5FtpProxyType.AuthResp</dt>
221 <dd>
222 USER user@remote.host<br/>
223 PASS pass<br/>
224 AUTH pruser<br/>
225 RESP prpass
226 </dd>
227 <dt>E5FtpProxyType.Bluecoat</dt>
228 <dd>
229 USER user@remote.host pruser<br/>
230 PASS pass<br/>
231 ACCT prpass
232 </dd>
233 </dl>
234
235 @param user username for the remote host (string)
236 @param password password for the remote host (string)
237 @param acct accounting information for the remote host (string)
238 @return response sent by the remote host (string)
239 @exception E5FtpProxyError raised to indicate a proxy related issue
240 """
241 if not user:
242 user = "anonymous"
243 if not password:
244 # make sure it is a string
245 password = ""
246 if not acct:
247 # make sure it is a string
248 acct = ""
249 if user == "anonymous" and password in {'', '-'}:
250 password += "anonymous@"
251
252 if self.__proxyType != E5FtpProxyType.NoProxy:
253 if self.__proxyType != E5FtpProxyType.NonAuthorizing:
254 # check, if a valid proxy configuration is known
255 if not self.__proxyUser:
256 raise E5FtpProxyError(
257 "991 Proxy usage requested, but no proxy user given")
258 if not self.__proxyPassword:
259 raise E5FtpProxyError(
260 "992 Proxy usage requested, but no proxy password given")
261
262 if self.__proxyType in [E5FtpProxyType.NonAuthorizing,
263 E5FtpProxyType.AuthResp, E5FtpProxyType.Bluecoat]:
264 user += "@" + self.__host
265 if self.__proxyType == E5FtpProxyType.Bluecoat:
266 user += " " + self.__proxyUser
267 acct = self.__proxyPassword
268 elif self.__proxyType == E5FtpProxyType.UserAtProxyuserAtServer:
269 user = "{0}@{1}@{2}".format(user, self.__proxyUser, self.__host)
270 password = "{0}@{1}".format(password, self.__proxyPassword)
271 else:
272 pruser = self.__proxyUser
273 if self.__proxyType == E5FtpProxyType.UserAtServer:
274 user += "@" + self.__host
275 elif self.__proxyType == E5FtpProxyType.ProxyuserAtServer:
276 pruser += "@" + self.__host
277
278 # authenticate to the proxy first
279 presp = self.sendcmd("USER " + pruser)
280 if presp[0] == "3":
281 presp = self.sendcmd("PASS " + self.__proxyPassword)
282 if presp[0] == "3" and self.__proxyAccount:
283 presp = self.sendcmd("ACCT " + self.__proxyAccount)
284 if presp[0] != "2":
285 raise E5FtpProxyError("9{0}0 Error authorizing at proxy\n{1}".format(
286 presp[0], presp))
287
288 if self.__proxyType == E5FtpProxyType.Site:
289 # send SITE command
290 presp = self.sendcmd("SITE " + self.__host)
291 if presp[0] != "2":
292 raise E5FtpProxyError(
293 "9{0}0 Error sending SITE command\n{1}".format(
294 presp[0], presp))
295 elif self.__proxyType == E5FtpProxyType.Open:
296 # send OPEN command
297 presp = self.sendcmd("OPEN " + self.__host)
298 if presp[0] != "2":
299 raise E5FtpProxyError(
300 "9{0}0 Error sending OPEN command\n{1}".format(
301 presp[0], presp))
302
303 # authenticate to the remote host or combined to proxy and remote host
304 resp = self.sendcmd("USER " + user)
305 if resp[0] == "3":
306 resp = self.sendcmd("PASS " + password)
307 if resp[0] == "3":
308 resp = self.sendcmd("ACCT " + acct)
309 if resp[0] != "2":
310 raise ftplib.error_reply(resp)
311
312 if self.__proxyType == E5FtpProxyType.AuthResp:
313 # authorize to the FTP proxy
314 presp = self.sendcmd("AUTH " + self.__proxyUser)
315 if presp[0] == "3":
316 presp = self.sendcmd("RESP " + self.__proxyPassword)
317 if presp[0] != "2":
318 raise E5FtpProxyError("9{0}0 Error authorizing at proxy\n{1}".format(
319 presp[0], presp))
320
321 return resp

eric ide

mercurial