8 """ |
8 """ |
9 |
9 |
10 import ftplib |
10 import ftplib |
11 import socket |
11 import socket |
12 import errno |
12 import errno |
13 |
13 import mimetypes |
14 from PyQt4.QtCore import QByteArray, QIODevice, Qt, QUrl, QTimer, QBuffer, QDate, QTime, \ |
14 |
15 QDateTime, QCoreApplication |
15 from PyQt4.QtCore import QByteArray, QIODevice, Qt, QUrl, QTimer, QBuffer, \ |
16 from PyQt4.QtGui import QPixmap |
16 QCoreApplication |
17 from PyQt4.QtNetwork import QNetworkReply, QNetworkRequest, QUrlInfo |
17 from PyQt4.QtGui import QPixmap, QDialog |
|
18 from PyQt4.QtNetwork import QNetworkReply, QNetworkRequest |
18 from PyQt4.QtWebKit import QWebSettings |
19 from PyQt4.QtWebKit import QWebSettings |
19 |
20 |
|
21 import Helpviewer.HelpWindow |
|
22 |
|
23 from UI.AuthenticationDialog import AuthenticationDialog |
|
24 |
|
25 import Preferences |
20 import UI.PixmapCache |
26 import UI.PixmapCache |
|
27 from Utilities.FtpUtilities import FtpDirLineParser, FtpDirLineParserError |
21 |
28 |
22 ftpListPage_html = """\ |
29 ftpListPage_html = """\ |
23 <?xml version="1.0" encoding="UTF-8" ?> |
30 <?xml version="1.0" encoding="UTF-8" ?> |
24 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
31 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
25 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
32 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
177 |
185 |
178 def __doFtpCommands(self): |
186 def __doFtpCommands(self): |
179 """ |
187 """ |
180 Private slot doing the sequence of FTP commands to get the requested result. |
188 Private slot doing the sequence of FTP commands to get the requested result. |
181 """ |
189 """ |
|
190 retry = True |
182 try: |
191 try: |
183 self.__ftp.connect(self.url().host(), timeout=10) |
192 while retry: |
184 self.__ftp.login(self.url().userName(), self.url().password()) |
193 self.__ftp.connect(self.url().host(), self.url().port(ftplib.FTP_PORT), |
185 self.__ftp.retrlines("LIST " + self.url().path(), self.__dirCallback) |
194 timeout=10) |
186 if len(self.__items) == 1 and \ |
195 ok, retry = self.__doFtpLogin(self.url().userName(), self.url().password()) |
187 self.__items[0].isFile(): |
196 if ok: |
188 self.__setContent() |
197 self.__ftp.retrlines("LIST " + self.url().path(), self.__dirCallback) |
189 self.__ftp.retrbinary("RETR " + self.url().path(), self.__retrCallback) |
198 if len(self.__items) == 1 and \ |
190 self.__content.append(512 * b' ') |
199 self.__items[0].isFile(): |
191 self.readyRead.emit() |
200 self.__setContent() |
192 else: |
201 self.__ftp.retrbinary("RETR " + self.url().path(), self.__retrCallback) |
193 self.__setListContent() |
202 self.__content.append(512 * b' ') |
194 self.__ftp.quit() |
203 self.readyRead.emit() |
|
204 else: |
|
205 self.__setListContent() |
|
206 self.__ftp.quit() |
195 except ftplib.all_errors as err: |
207 except ftplib.all_errors as err: |
196 if isinstance(err, socket.gaierror): |
208 if isinstance(err, socket.gaierror): |
197 errCode = QNetworkReply.HostNotFoundError |
209 errCode = QNetworkReply.HostNotFoundError |
198 elif isinstance(err, socket.error) and err.errno == errno.ECONNREFUSED: |
210 elif isinstance(err, socket.error) and err.errno == errno.ECONNREFUSED: |
199 errCode = QNetworkReply.ConnectionRefusedError |
211 errCode = QNetworkReply.ConnectionRefusedError |
201 errCode = QNetworkReply.ProtocolFailure |
213 errCode = QNetworkReply.ProtocolFailure |
202 self.setError(errCode, str(err)) |
214 self.setError(errCode, str(err)) |
203 self.error.emit(errCode) |
215 self.error.emit(errCode) |
204 self.finished.emit() |
216 self.finished.emit() |
205 |
217 |
|
218 def __doFtpLogin(self, username, password): |
|
219 """ |
|
220 Private method to do the FTP login with asking for a username and password, |
|
221 if the login fails with an error 530. |
|
222 |
|
223 @param username user name to use for the login (string) |
|
224 @param password password to use for the login (string) |
|
225 @return tuple of two flags indicating a successful login and |
|
226 if the login should be retried (boolean, boolean) |
|
227 """ |
|
228 try: |
|
229 self.__ftp.login(username, password) |
|
230 return True, False |
|
231 except ftplib.error_perm as err: |
|
232 code, msg = err.args[0].split(None, 1) |
|
233 if code.strip() == "530": |
|
234 # error 530 -> Login incorrect |
|
235 urlRoot = "{0}://{1}"\ |
|
236 .format(self.url().scheme(), self.url().authority()) |
|
237 info = self.trUtf8("<b>Enter username and password for '{0}'</b>")\ |
|
238 .format(urlRoot) |
|
239 dlg = AuthenticationDialog(info, self.url().userName(), |
|
240 Preferences.getUser("SavePasswords"), |
|
241 Preferences.getUser("SavePasswords")) |
|
242 if Preferences.getUser("SavePasswords"): |
|
243 username, password = \ |
|
244 Helpviewer.HelpWindow.HelpWindow.passwordManager().getLogin( |
|
245 self.url(), "") |
|
246 if username: |
|
247 dlg.setData(username, password) |
|
248 if dlg.exec_() == QDialog.Accepted: |
|
249 username, password = dlg.getData() |
|
250 if Preferences.getUser("SavePasswords"): |
|
251 Helpviewer.HelpWindow.HelpWindow.passwordManager().setLogin( |
|
252 self.url(), "", username, password) |
|
253 url = self.url() |
|
254 url.setUserName(username) |
|
255 url.setPassword(password) |
|
256 self.setUrl(url) |
|
257 return False, True |
|
258 else: |
|
259 return False, False |
|
260 else: |
|
261 raise |
|
262 |
206 def __dirCallback(self, line): |
263 def __dirCallback(self, line): |
207 """ |
264 """ |
208 Private slot handling the receipt of directory listings. |
265 Private slot handling the receipt of directory listings. |
209 |
266 |
210 @param line the received line of the directory listing (string) |
267 @param line the received line of the directory listing (string) |
211 """ |
268 """ |
212 words = line.split(None, 8) |
269 try: |
213 if len(words) < 6: |
270 urlInfo = self.__dirLineParser.parseLine(line) |
214 # skip short lines |
271 except FtpDirLineParserError: |
215 return |
272 # silently ignore parser errors |
216 filename = words[-1].lstrip() |
273 urlInfo = None |
217 i = filename.find(" -> ") |
274 |
218 if i >= 0: |
275 if urlInfo: |
219 filename = filename[:i] |
276 self.__items.append(urlInfo) |
220 infostuff = words[-5:-1] |
|
221 mode = words[0].strip() |
|
222 |
|
223 info = QUrlInfo() |
|
224 # 1. type of item |
|
225 if mode[0] == "d": |
|
226 info.setDir(True) |
|
227 info.setFile(False) |
|
228 elif mode[0] == "l": |
|
229 info.setSymLink(True) |
|
230 elif mode[0] == "-": |
|
231 info.setDir(False) |
|
232 info.setFile(True) |
|
233 # 2. name |
|
234 info.setName(filename.strip()) |
|
235 # 3. size |
|
236 if mode[0] == "-": |
|
237 info.setSize(int(infostuff[0])) |
|
238 # 4. last modified |
|
239 if infostuff[1] in self.Monthnames2Int: |
|
240 month = self.Monthnames2Int[infostuff[1]] |
|
241 else: |
|
242 month = 1 |
|
243 if ":" in infostuff[3]: |
|
244 # year is current year |
|
245 year = QDate.currentDate().year() |
|
246 timeStr = infostuff[3] |
|
247 else: |
|
248 year = int(infostuff[3]) |
|
249 timeStr = "00:00" |
|
250 date = QDate(year, month, int(infostuff[2])) |
|
251 time = QTime.fromString(timeStr, "hh:mm") |
|
252 lastModified = QDateTime(date, time) |
|
253 info.setLastModified(lastModified) |
|
254 |
|
255 self.__items.append(info) |
|
256 |
277 |
257 QCoreApplication.processEvents() |
278 QCoreApplication.processEvents() |
258 |
279 |
259 def __retrCallback(self, data): |
280 def __retrCallback(self, data): |
260 """ |
281 """ |
268 |
289 |
269 def __setContent(self): |
290 def __setContent(self): |
270 """ |
291 """ |
271 Private method to finish the setup of the data. |
292 Private method to finish the setup of the data. |
272 """ |
293 """ |
|
294 mtype, encoding = mimetypes.guess_type(self.url().toString()) |
273 self.open(QIODevice.ReadOnly | QIODevice.Unbuffered) |
295 self.open(QIODevice.ReadOnly | QIODevice.Unbuffered) |
274 self.setHeader(QNetworkRequest.ContentLengthHeader, self.__items[0].size()) |
296 self.setHeader(QNetworkRequest.ContentLengthHeader, self.__items[0].size()) |
|
297 if mtype: |
|
298 self.setHeader(QNetworkRequest.ContentTypeHeader, mtype) |
275 self.setAttribute(QNetworkRequest.HttpStatusCodeAttribute, 200) |
299 self.setAttribute(QNetworkRequest.HttpStatusCodeAttribute, 200) |
276 self.setAttribute(QNetworkRequest.HttpReasonPhraseAttribute, "Ok") |
300 self.setAttribute(QNetworkRequest.HttpReasonPhraseAttribute, "Ok") |
277 self.metaDataChanged.emit() |
301 self.metaDataChanged.emit() |
278 |
302 |
279 def __cssLinkClass(self, icon, size=32): |
303 def __cssLinkClass(self, icon, size=32): |