Helpviewer/Network/FtpReply.py

changeset 2044
9d229b584c49
parent 1625
4f03e45703e9
child 2047
739aa1717df5
--- a/Helpviewer/Network/FtpReply.py	Fri Sep 14 19:57:52 2012 +0200
+++ b/Helpviewer/Network/FtpReply.py	Sat Sep 15 21:04:39 2012 +0200
@@ -7,10 +7,14 @@
 Module implementing a network reply class for FTP resources.
 """
 
-from PyQt4.QtCore import QByteArray, QIODevice, Qt, QUrl, QTimer, QBuffer
+import ftplib
+import socket
+import errno
+
+from PyQt4.QtCore import QByteArray, QIODevice, Qt, QUrl, QTimer, QBuffer, QDate, QTime, \
+    QDateTime, QCoreApplication
 from PyQt4.QtGui import QPixmap
-from PyQt4.QtNetwork import QFtp, QNetworkReply, QNetworkRequest, QUrlInfo, \
-    QNetworkProxyQuery, QNetworkProxy, QAuthenticator
+from PyQt4.QtNetwork import QNetworkReply, QNetworkRequest, QUrlInfo
 from PyQt4.QtWebKit import QWebSettings
 
 import UI.PixmapCache
@@ -95,6 +99,21 @@
     """
     Class implementing a network reply for FTP resources.
     """
+    Monthnames2Int = {
+        "Jan": 1,
+        "Feb": 2,
+        "Mar": 3,
+        "Apr": 4,
+        "May": 5,
+        "Jun": 6,
+        "Jul": 7,
+        "Aug": 8,
+        "Sep": 9,
+        "Oct": 10,
+        "Nov": 11,
+        "Dec": 12,
+    }
+    
     def __init__(self, url, parent=None):
         """
         Constructor
@@ -106,12 +125,7 @@
         
         self.__manager = parent
         
-        self.__ftp = QFtp(self)
-        self.__ftp.listInfo.connect(self.__processListInfo)
-        self.__ftp.readyRead.connect(self.__processData)
-        self.__ftp.commandFinished.connect(self.__processCommand)
-        self.__ftp.commandStarted.connect(self.__commandStarted)
-        self.__ftp.dataTransferProgress.connect(self.downloadProgress)
+        self.__ftp = ftplib.FTP()
         
         self.__items = []
         self.__content = QByteArray()
@@ -120,33 +134,10 @@
         if url.path() == "":
             url.setPath("/")
         self.setUrl(url)
-        # do proxy setup
-        query = QNetworkProxyQuery(url)
-        proxyList = parent.proxyFactory().queryProxy(query)
-        ftpProxy = QNetworkProxy()
-        for proxy in proxyList:
-            if proxy.type() == QNetworkProxy.NoProxy or \
-               proxy.type() == QNetworkProxy.FtpCachingProxy:
-                ftpProxy = proxy
-                break
-        if ftpProxy.type() == QNetworkProxy.DefaultProxy:
-            self.setError(QNetworkReply.ProxyNotFoundError,
-                          self.trUtf8("No suitable proxy found."))
-            QTimer.singleShot(0, self.__errorSignals)
-            return
-        elif ftpProxy.type() == QNetworkProxy.FtpCachingProxy:
-            self.__ftp.setProxy(ftpProxy.hostName(), ftpProxy.port())
         
         self.__loggingIn = False
         
-        QTimer.singleShot(0, self.__connectToHost)
-    
-    def __errorSignals(self):
-        """
-        Private slot to send signal for errors during initialisation.
-        """
-        self.error.emit(QNetworkReply.ProxyNotFoundError)
-        self.finished.emit()
+        QTimer.singleShot(0, self.__doFtpCommands)
     
     def abort(self):
         """
@@ -184,96 +175,96 @@
             self.__content.remove(0, len_)
             return buffer
     
-    def __connectToHost(self):
-        """
-        Private slot to start the FTP process by connecting to the host.
-        """
-        self.__ftp.connectToHost(self.url().host())
-    
-    def __commandStarted(self, id):
+    def __doFtpCommands(self):
         """
-        Private slot to handle the start of FTP commands.
-        
-        @param id id of the command to be processed (integer) (ignored)
-        """
-        cmd = self.__ftp.currentCommand()
-        if cmd == QFtp.Get:
-            self.__setContent()
-    
-    def __processCommand(self, id, error):
-        """
-        Private slot to handle the end of FTP commands.
-        
-        @param id id of the command to be processed (integer) (ignored)
-        @param error flag indicating an error condition (boolean)
+        Private slot doing the sequence of FTP commands to get the requested result.
         """
-        if error:
-            if self.__ftp.error() == QFtp.HostNotFound:
-                err = QNetworkReply.HostNotFoundError
-            elif self.__ftp.error() == QFtp.ConnectionRefused:
-                err = QNetworkReply.ConnectionRefusedError
-            else:
-                if self.__loggingIn and \
-                   self.__ftp.state() == QFtp.Connected:
-                    # authentication is required
-                    if "anonymous" in self.__ftp.errorString():
-                        self.__ftp.login()
-                        return
-                    
-                    newUrl = self.url()
-                    auth = QAuthenticator()
-                    self.__manager.authenticationRequired.emit(self, auth)
-                    if not auth.isNull():
-                        if auth.user():
-                            newUrl.setUserName(auth.user())
-                            newUrl.setPassword(auth.password())
-                            self.setUrl(newUrl)
-                        else:
-                            auth.setUser("anonymous")
-                            auth.setPassword("anonymous")
-                        if self.__ftp.state() == QFtp.Connected:
-                            self.__ftp.login(auth.user(), auth.password())
-                            return
-                
-                err = QNetworkReply.ProtocolFailure
-            self.setError(err, self.__ftp.errorString())
-            self.error.emit(err)
-            self.finished.emit()
-            if self.__ftp.state() not in [QFtp.Unconnected, QFtp.Closing]:
-                self.__ftp.close()
-            return
-        
-        cmd = self.__ftp.currentCommand()
-        if cmd == QFtp.ConnectToHost:
-            self.__loggingIn = True
+        try:
+            self.__ftp.connect(self.url().host(), timeout=10)
             self.__ftp.login(self.url().userName(), self.url().password())
-        elif cmd == QFtp.Login:
-            self.__loggingIn = False
-            self.__ftp.list(self.url().path())
-        elif cmd == QFtp.List:
+            self.__ftp.retrlines("LIST " + self.url().path(), self.__dirCallback)
             if len(self.__items) == 1 and \
                self.__items[0].isFile():
-                self.__ftp.get(self.url().path())
+                self.__setContent()
+                self.__ftp.retrbinary("RETR " + self.url().path(), self.__retrCallback)
+                self.__content.append(512 * b' ')
+                self.readyRead.emit()
             else:
                 self.__setListContent()
-        elif cmd == QFtp.Get:
-            self.finished.emit()
-            self.__ftp.close()
+            self.__ftp.quit()
+        except ftplib.all_errors as err:
+            if isinstance(err, socket.gaierror):
+                errCode = QNetworkReply.HostNotFoundError
+            elif isinstance(err, socket.error) and err.errno == errno.ECONNREFUSED:
+                errCode = QNetworkReply.ConnectionRefusedError
+            else:
+                errCode = QNetworkReply.ProtocolFailure
+            self.setError(errCode, str(err))
+            self.error.emit(errCode)
+        self.finished.emit()
     
-    def __processListInfo(self, urlInfo):
+    def __dirCallback(self, line):
+        """
+        Private slot handling the receipt of directory listings.
+        
+        @param line the received line of the directory listing (string)
         """
-        Private slot to process list information from the FTP server.
+        words = line.split(None, 8)
+        if len(words) < 6:
+            # skip short lines
+            return
+        filename = words[-1].lstrip()
+        i = filename.find(" -> ")
+        if i >= 0:
+            filename = filename[:i]
+        infostuff = words[-5:-1]
+        mode = words[0].strip()
         
-        @param urlInfo reference to the information object (QUrlInfo)
-        """
-        self.__items.append(QUrlInfo(urlInfo))
+        info = QUrlInfo()
+        # 1. type of item
+        if mode[0] == "d":
+            info.setDir(True)
+            info.setFile(False)
+        elif mode[0] == "l":
+            info.setSymLink(True)
+        elif mode[0] == "-":
+            info.setDir(False)
+            info.setFile(True)
+        # 2. name
+        info.setName(filename.strip())
+        # 3. size
+        if mode[0] == "-":
+            info.setSize(int(infostuff[0]))
+        # 4. last modified
+        if infostuff[1] in self.Monthnames2Int:
+            month = self.Monthnames2Int[infostuff[1]]
+        else:
+            month = 1
+        if ":" in infostuff[3]:
+            # year is current year
+            year = QDate.currentDate().year()
+            timeStr = infostuff[3]
+        else:
+            year = int(infostuff[3])
+            timeStr = "00:00"
+        date = QDate(year, month, int(infostuff[2]))
+        time = QTime.fromString(timeStr, "hh:mm")
+        lastModified = QDateTime(date, time)
+        info.setLastModified(lastModified)
+        
+        self.__items.append(info)
+        
+        QCoreApplication.processEvents()
     
-    def __processData(self):
+    def __retrCallback(self, data):
         """
-        Private slot to process data from the FTP server.
+        Private slot handling the reception of data.
+        
+        @param data data received from the FTP server (bytes)
         """
-        self.__content += self.__ftp.readAll()
-        self.readyRead.emit()
+        self.__content += QByteArray(data)
+        
+        QCoreApplication.processEvents()
     
     def __setContent(self):
         """
@@ -401,6 +392,7 @@
             table
         )
         self.__content = QByteArray(content.encode("utf8"))
+        self.__content.append(512 * b' ')
         
         self.open(QIODevice.ReadOnly | QIODevice.Unbuffered)
         self.setHeader(QNetworkRequest.ContentTypeHeader, "text/html; charset=UTF-8")
@@ -410,5 +402,3 @@
         self.metaDataChanged.emit()
         self.downloadProgress.emit(self.__content.size(), self.__content.size())
         self.readyRead.emit()
-        self.finished.emit()
-        self.__ftp.close()

eric ide

mercurial