src/eric7/EricNetwork/EricFtp.py

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

eric ide

mercurial