Helpviewer/Network/FtpReply.py

changeset 2044
9d229b584c49
parent 1625
4f03e45703e9
child 2047
739aa1717df5
equal deleted inserted replaced
2042:cbe46853273e 2044:9d229b584c49
5 5
6 """ 6 """
7 Module implementing a network reply class for FTP resources. 7 Module implementing a network reply class for FTP resources.
8 """ 8 """
9 9
10 from PyQt4.QtCore import QByteArray, QIODevice, Qt, QUrl, QTimer, QBuffer 10 import ftplib
11 import socket
12 import errno
13
14 from PyQt4.QtCore import QByteArray, QIODevice, Qt, QUrl, QTimer, QBuffer, QDate, QTime, \
15 QDateTime, QCoreApplication
11 from PyQt4.QtGui import QPixmap 16 from PyQt4.QtGui import QPixmap
12 from PyQt4.QtNetwork import QFtp, QNetworkReply, QNetworkRequest, QUrlInfo, \ 17 from PyQt4.QtNetwork import QNetworkReply, QNetworkRequest, QUrlInfo
13 QNetworkProxyQuery, QNetworkProxy, QAuthenticator
14 from PyQt4.QtWebKit import QWebSettings 18 from PyQt4.QtWebKit import QWebSettings
15 19
16 import UI.PixmapCache 20 import UI.PixmapCache
17 21
18 ftpListPage_html = """\ 22 ftpListPage_html = """\
93 97
94 class FtpReply(QNetworkReply): 98 class FtpReply(QNetworkReply):
95 """ 99 """
96 Class implementing a network reply for FTP resources. 100 Class implementing a network reply for FTP resources.
97 """ 101 """
102 Monthnames2Int = {
103 "Jan": 1,
104 "Feb": 2,
105 "Mar": 3,
106 "Apr": 4,
107 "May": 5,
108 "Jun": 6,
109 "Jul": 7,
110 "Aug": 8,
111 "Sep": 9,
112 "Oct": 10,
113 "Nov": 11,
114 "Dec": 12,
115 }
116
98 def __init__(self, url, parent=None): 117 def __init__(self, url, parent=None):
99 """ 118 """
100 Constructor 119 Constructor
101 120
102 @param url requested FTP URL (QUrl) 121 @param url requested FTP URL (QUrl)
104 """ 123 """
105 super().__init__(parent) 124 super().__init__(parent)
106 125
107 self.__manager = parent 126 self.__manager = parent
108 127
109 self.__ftp = QFtp(self) 128 self.__ftp = ftplib.FTP()
110 self.__ftp.listInfo.connect(self.__processListInfo)
111 self.__ftp.readyRead.connect(self.__processData)
112 self.__ftp.commandFinished.connect(self.__processCommand)
113 self.__ftp.commandStarted.connect(self.__commandStarted)
114 self.__ftp.dataTransferProgress.connect(self.downloadProgress)
115 129
116 self.__items = [] 130 self.__items = []
117 self.__content = QByteArray() 131 self.__content = QByteArray()
118 self.__units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] 132 self.__units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
119 133
120 if url.path() == "": 134 if url.path() == "":
121 url.setPath("/") 135 url.setPath("/")
122 self.setUrl(url) 136 self.setUrl(url)
123 # do proxy setup
124 query = QNetworkProxyQuery(url)
125 proxyList = parent.proxyFactory().queryProxy(query)
126 ftpProxy = QNetworkProxy()
127 for proxy in proxyList:
128 if proxy.type() == QNetworkProxy.NoProxy or \
129 proxy.type() == QNetworkProxy.FtpCachingProxy:
130 ftpProxy = proxy
131 break
132 if ftpProxy.type() == QNetworkProxy.DefaultProxy:
133 self.setError(QNetworkReply.ProxyNotFoundError,
134 self.trUtf8("No suitable proxy found."))
135 QTimer.singleShot(0, self.__errorSignals)
136 return
137 elif ftpProxy.type() == QNetworkProxy.FtpCachingProxy:
138 self.__ftp.setProxy(ftpProxy.hostName(), ftpProxy.port())
139 137
140 self.__loggingIn = False 138 self.__loggingIn = False
141 139
142 QTimer.singleShot(0, self.__connectToHost) 140 QTimer.singleShot(0, self.__doFtpCommands)
143
144 def __errorSignals(self):
145 """
146 Private slot to send signal for errors during initialisation.
147 """
148 self.error.emit(QNetworkReply.ProxyNotFoundError)
149 self.finished.emit()
150 141
151 def abort(self): 142 def abort(self):
152 """ 143 """
153 Public slot to abort the operation. 144 Public slot to abort the operation.
154 """ 145 """
182 len_ = min(maxlen, self.__content.size()) 173 len_ = min(maxlen, self.__content.size())
183 buffer = bytes(self.__content[:len_]) 174 buffer = bytes(self.__content[:len_])
184 self.__content.remove(0, len_) 175 self.__content.remove(0, len_)
185 return buffer 176 return buffer
186 177
187 def __connectToHost(self): 178 def __doFtpCommands(self):
188 """ 179 """
189 Private slot to start the FTP process by connecting to the host. 180 Private slot doing the sequence of FTP commands to get the requested result.
190 """ 181 """
191 self.__ftp.connectToHost(self.url().host()) 182 try:
192 183 self.__ftp.connect(self.url().host(), timeout=10)
193 def __commandStarted(self, id):
194 """
195 Private slot to handle the start of FTP commands.
196
197 @param id id of the command to be processed (integer) (ignored)
198 """
199 cmd = self.__ftp.currentCommand()
200 if cmd == QFtp.Get:
201 self.__setContent()
202
203 def __processCommand(self, id, error):
204 """
205 Private slot to handle the end of FTP commands.
206
207 @param id id of the command to be processed (integer) (ignored)
208 @param error flag indicating an error condition (boolean)
209 """
210 if error:
211 if self.__ftp.error() == QFtp.HostNotFound:
212 err = QNetworkReply.HostNotFoundError
213 elif self.__ftp.error() == QFtp.ConnectionRefused:
214 err = QNetworkReply.ConnectionRefusedError
215 else:
216 if self.__loggingIn and \
217 self.__ftp.state() == QFtp.Connected:
218 # authentication is required
219 if "anonymous" in self.__ftp.errorString():
220 self.__ftp.login()
221 return
222
223 newUrl = self.url()
224 auth = QAuthenticator()
225 self.__manager.authenticationRequired.emit(self, auth)
226 if not auth.isNull():
227 if auth.user():
228 newUrl.setUserName(auth.user())
229 newUrl.setPassword(auth.password())
230 self.setUrl(newUrl)
231 else:
232 auth.setUser("anonymous")
233 auth.setPassword("anonymous")
234 if self.__ftp.state() == QFtp.Connected:
235 self.__ftp.login(auth.user(), auth.password())
236 return
237
238 err = QNetworkReply.ProtocolFailure
239 self.setError(err, self.__ftp.errorString())
240 self.error.emit(err)
241 self.finished.emit()
242 if self.__ftp.state() not in [QFtp.Unconnected, QFtp.Closing]:
243 self.__ftp.close()
244 return
245
246 cmd = self.__ftp.currentCommand()
247 if cmd == QFtp.ConnectToHost:
248 self.__loggingIn = True
249 self.__ftp.login(self.url().userName(), self.url().password()) 184 self.__ftp.login(self.url().userName(), self.url().password())
250 elif cmd == QFtp.Login: 185 self.__ftp.retrlines("LIST " + self.url().path(), self.__dirCallback)
251 self.__loggingIn = False
252 self.__ftp.list(self.url().path())
253 elif cmd == QFtp.List:
254 if len(self.__items) == 1 and \ 186 if len(self.__items) == 1 and \
255 self.__items[0].isFile(): 187 self.__items[0].isFile():
256 self.__ftp.get(self.url().path()) 188 self.__setContent()
189 self.__ftp.retrbinary("RETR " + self.url().path(), self.__retrCallback)
190 self.__content.append(512 * b' ')
191 self.readyRead.emit()
257 else: 192 else:
258 self.__setListContent() 193 self.__setListContent()
259 elif cmd == QFtp.Get: 194 self.__ftp.quit()
260 self.finished.emit() 195 except ftplib.all_errors as err:
261 self.__ftp.close() 196 if isinstance(err, socket.gaierror):
262 197 errCode = QNetworkReply.HostNotFoundError
263 def __processListInfo(self, urlInfo): 198 elif isinstance(err, socket.error) and err.errno == errno.ECONNREFUSED:
264 """ 199 errCode = QNetworkReply.ConnectionRefusedError
265 Private slot to process list information from the FTP server. 200 else:
266 201 errCode = QNetworkReply.ProtocolFailure
267 @param urlInfo reference to the information object (QUrlInfo) 202 self.setError(errCode, str(err))
268 """ 203 self.error.emit(errCode)
269 self.__items.append(QUrlInfo(urlInfo)) 204 self.finished.emit()
270 205
271 def __processData(self): 206 def __dirCallback(self, line):
272 """ 207 """
273 Private slot to process data from the FTP server. 208 Private slot handling the receipt of directory listings.
274 """ 209
275 self.__content += self.__ftp.readAll() 210 @param line the received line of the directory listing (string)
276 self.readyRead.emit() 211 """
212 words = line.split(None, 8)
213 if len(words) < 6:
214 # skip short lines
215 return
216 filename = words[-1].lstrip()
217 i = filename.find(" -> ")
218 if i >= 0:
219 filename = filename[:i]
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
257 QCoreApplication.processEvents()
258
259 def __retrCallback(self, data):
260 """
261 Private slot handling the reception of data.
262
263 @param data data received from the FTP server (bytes)
264 """
265 self.__content += QByteArray(data)
266
267 QCoreApplication.processEvents()
277 268
278 def __setContent(self): 269 def __setContent(self):
279 """ 270 """
280 Private method to finish the setup of the data. 271 Private method to finish the setup of the data.
281 """ 272 """
399 self.trUtf8("Listing of {0}").format(basePath), 390 self.trUtf8("Listing of {0}").format(basePath),
400 parentStr, 391 parentStr,
401 table 392 table
402 ) 393 )
403 self.__content = QByteArray(content.encode("utf8")) 394 self.__content = QByteArray(content.encode("utf8"))
395 self.__content.append(512 * b' ')
404 396
405 self.open(QIODevice.ReadOnly | QIODevice.Unbuffered) 397 self.open(QIODevice.ReadOnly | QIODevice.Unbuffered)
406 self.setHeader(QNetworkRequest.ContentTypeHeader, "text/html; charset=UTF-8") 398 self.setHeader(QNetworkRequest.ContentTypeHeader, "text/html; charset=UTF-8")
407 self.setHeader(QNetworkRequest.ContentLengthHeader, self.__content.size()) 399 self.setHeader(QNetworkRequest.ContentLengthHeader, self.__content.size())
408 self.setAttribute(QNetworkRequest.HttpStatusCodeAttribute, 200) 400 self.setAttribute(QNetworkRequest.HttpStatusCodeAttribute, 200)
409 self.setAttribute(QNetworkRequest.HttpReasonPhraseAttribute, "Ok") 401 self.setAttribute(QNetworkRequest.HttpReasonPhraseAttribute, "Ok")
410 self.metaDataChanged.emit() 402 self.metaDataChanged.emit()
411 self.downloadProgress.emit(self.__content.size(), self.__content.size()) 403 self.downloadProgress.emit(self.__content.size(), self.__content.size())
412 self.readyRead.emit() 404 self.readyRead.emit()
413 self.finished.emit()
414 self.__ftp.close()

eric ide

mercurial