Helpviewer/Network/FtpReply.py

changeset 2047
739aa1717df5
parent 2044
9d229b584c49
child 2050
585f6646bf50
equal deleted inserted replaced
2046:a4d88430034e 2047:739aa1717df5
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">
128 self.__ftp = ftplib.FTP() 135 self.__ftp = ftplib.FTP()
129 136
130 self.__items = [] 137 self.__items = []
131 self.__content = QByteArray() 138 self.__content = QByteArray()
132 self.__units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] 139 self.__units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
140 self.__dirLineParser = FtpDirLineParser()
133 141
134 if url.path() == "": 142 if url.path() == "":
135 url.setPath("/") 143 url.setPath("/")
136 self.setUrl(url) 144 self.setUrl(url)
137 145
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):

eric ide

mercurial