eric6/E5Network/E5Ftp.py

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

eric ide

mercurial