Change the FtpReply class to use Python ftplib instead of QFtp because the later is not available in Qt5 anymore.

Sat, 15 Sep 2012 21:04:39 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 15 Sep 2012 21:04:39 +0200
changeset 2044
9d229b584c49
parent 2042
cbe46853273e
child 2045
c46cd1cdefd8

Change the FtpReply class to use Python ftplib instead of QFtp because the later is not available in Qt5 anymore.

APIs/Python3/eric5.api file | annotate | diff | comparison | revisions
Documentation/Help/source.qch file | annotate | diff | comparison | revisions
Documentation/Help/source.qhp file | annotate | diff | comparison | revisions
Documentation/Source/eric5.Helpviewer.Network.FtpReply.html file | annotate | diff | comparison | revisions
Helpviewer/Network/FtpReply.py file | annotate | diff | comparison | revisions
--- a/APIs/Python3/eric5.api	Fri Sep 14 19:57:52 2012 +0200
+++ b/APIs/Python3/eric5.api	Sat Sep 15 21:04:39 2012 +0200
@@ -2621,6 +2621,7 @@
 eric5.Helpviewer.Network.FollowRedirectReply.FollowRedirectReply.url?4()
 eric5.Helpviewer.Network.FollowRedirectReply.FollowRedirectReply?1(url, manager, maxRedirects=5)
 eric5.Helpviewer.Network.FtpAccessHandler.FtpAccessHandler.createRequest?4(op, request, outgoingData=None)
+eric5.Helpviewer.Network.FtpReply.FtpReply.Monthnames2Int?7
 eric5.Helpviewer.Network.FtpReply.FtpReply.abort?4()
 eric5.Helpviewer.Network.FtpReply.FtpReply.bytesAvailable?4()
 eric5.Helpviewer.Network.FtpReply.FtpReply.isSequential?4()
Binary file Documentation/Help/source.qch has changed
--- a/Documentation/Help/source.qhp	Fri Sep 14 19:57:52 2012 +0200
+++ b/Documentation/Help/source.qhp	Sat Sep 15 21:04:39 2012 +0200
@@ -4502,13 +4502,10 @@
       <keyword name="FtpReply" id="FtpReply" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply" />
       <keyword name="FtpReply (Constructor)" id="FtpReply (Constructor)" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.__init__" />
       <keyword name="FtpReply (Module)" id="FtpReply (Module)" ref="eric5.Helpviewer.Network.FtpReply.html" />
-      <keyword name="FtpReply.__commandStarted" id="FtpReply.__commandStarted" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.__commandStarted" />
-      <keyword name="FtpReply.__connectToHost" id="FtpReply.__connectToHost" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.__connectToHost" />
       <keyword name="FtpReply.__cssLinkClass" id="FtpReply.__cssLinkClass" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.__cssLinkClass" />
-      <keyword name="FtpReply.__errorSignals" id="FtpReply.__errorSignals" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.__errorSignals" />
-      <keyword name="FtpReply.__processCommand" id="FtpReply.__processCommand" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.__processCommand" />
-      <keyword name="FtpReply.__processData" id="FtpReply.__processData" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.__processData" />
-      <keyword name="FtpReply.__processListInfo" id="FtpReply.__processListInfo" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.__processListInfo" />
+      <keyword name="FtpReply.__dirCallback" id="FtpReply.__dirCallback" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.__dirCallback" />
+      <keyword name="FtpReply.__doFtpCommands" id="FtpReply.__doFtpCommands" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.__doFtpCommands" />
+      <keyword name="FtpReply.__retrCallback" id="FtpReply.__retrCallback" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.__retrCallback" />
       <keyword name="FtpReply.__setContent" id="FtpReply.__setContent" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.__setContent" />
       <keyword name="FtpReply.__setListContent" id="FtpReply.__setListContent" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.__setListContent" />
       <keyword name="FtpReply.abort" id="FtpReply.abort" ref="eric5.Helpviewer.Network.FtpReply.html#FtpReply.abort" />
--- a/Documentation/Source/eric5.Helpviewer.Network.FtpReply.html	Fri Sep 14 19:57:52 2012 +0200
+++ b/Documentation/Source/eric5.Helpviewer.Network.FtpReply.html	Sat Sep 15 21:04:39 2012 +0200
@@ -49,7 +49,7 @@
 QNetworkReply
 <h3>Class Attributes</h3>
 <table>
-<tr><td>None</td></tr>
+<tr><td>Monthnames2Int</td></tr>
 </table>
 <h3>Class Methods</h3>
 <table>
@@ -61,26 +61,17 @@
 <td><a href="#FtpReply.__init__">FtpReply</a></td>
 <td>Constructor</td>
 </tr><tr>
-<td><a href="#FtpReply.__commandStarted">__commandStarted</a></td>
-<td>Private slot to handle the start of FTP commands.</td>
-</tr><tr>
-<td><a href="#FtpReply.__connectToHost">__connectToHost</a></td>
-<td>Private slot to start the FTP process by connecting to the host.</td>
-</tr><tr>
 <td><a href="#FtpReply.__cssLinkClass">__cssLinkClass</a></td>
 <td>Private method to generate a link class with an icon.</td>
 </tr><tr>
-<td><a href="#FtpReply.__errorSignals">__errorSignals</a></td>
-<td>Private slot to send signal for errors during initialisation.</td>
-</tr><tr>
-<td><a href="#FtpReply.__processCommand">__processCommand</a></td>
-<td>Private slot to handle the end of FTP commands.</td>
+<td><a href="#FtpReply.__dirCallback">__dirCallback</a></td>
+<td>Private slot handling the receipt of directory listings.</td>
 </tr><tr>
-<td><a href="#FtpReply.__processData">__processData</a></td>
-<td>Private slot to process data from the FTP server.</td>
+<td><a href="#FtpReply.__doFtpCommands">__doFtpCommands</a></td>
+<td>Private slot doing the sequence of FTP commands to get the requested result.</td>
 </tr><tr>
-<td><a href="#FtpReply.__processListInfo">__processListInfo</a></td>
-<td>Private slot to process list information from the FTP server.</td>
+<td><a href="#FtpReply.__retrCallback">__retrCallback</a></td>
+<td>Private slot handling the reception of data.</td>
 </tr><tr>
 <td><a href="#FtpReply.__setContent">__setContent</a></td>
 <td>Private method to finish the setup of the data.</td>
@@ -118,22 +109,7 @@
 <dd>
 reference to the parent object (QObject)
 </dd>
-</dl><a NAME="FtpReply.__commandStarted" ID="FtpReply.__commandStarted"></a>
-<h4>FtpReply.__commandStarted</h4>
-<b>__commandStarted</b>(<i>id</i>)
-<p>
-        Private slot to handle the start of FTP commands.
-</p><dl>
-<dt><i>id</i></dt>
-<dd>
-id of the command to be processed (integer) (ignored)
-</dd>
-</dl><a NAME="FtpReply.__connectToHost" ID="FtpReply.__connectToHost"></a>
-<h4>FtpReply.__connectToHost</h4>
-<b>__connectToHost</b>(<i></i>)
-<p>
-        Private slot to start the FTP process by connecting to the host.
-</p><a NAME="FtpReply.__cssLinkClass" ID="FtpReply.__cssLinkClass"></a>
+</dl><a NAME="FtpReply.__cssLinkClass" ID="FtpReply.__cssLinkClass"></a>
 <h4>FtpReply.__cssLinkClass</h4>
 <b>__cssLinkClass</b>(<i>icon, size=32</i>)
 <p>
@@ -151,38 +127,30 @@
 <dd>
 CSS class string (string)
 </dd>
-</dl><a NAME="FtpReply.__errorSignals" ID="FtpReply.__errorSignals"></a>
-<h4>FtpReply.__errorSignals</h4>
-<b>__errorSignals</b>(<i></i>)
+</dl><a NAME="FtpReply.__dirCallback" ID="FtpReply.__dirCallback"></a>
+<h4>FtpReply.__dirCallback</h4>
+<b>__dirCallback</b>(<i>line</i>)
 <p>
-        Private slot to send signal for errors during initialisation.
-</p><a NAME="FtpReply.__processCommand" ID="FtpReply.__processCommand"></a>
-<h4>FtpReply.__processCommand</h4>
-<b>__processCommand</b>(<i>id, error</i>)
-<p>
-        Private slot to handle the end of FTP commands.
+        Private slot handling the receipt of directory listings.
 </p><dl>
-<dt><i>id</i></dt>
-<dd>
-id of the command to be processed (integer) (ignored)
-</dd><dt><i>error</i></dt>
+<dt><i>line</i></dt>
 <dd>
-flag indicating an error condition (boolean)
+the received line of the directory listing (string)
 </dd>
-</dl><a NAME="FtpReply.__processData" ID="FtpReply.__processData"></a>
-<h4>FtpReply.__processData</h4>
-<b>__processData</b>(<i></i>)
+</dl><a NAME="FtpReply.__doFtpCommands" ID="FtpReply.__doFtpCommands"></a>
+<h4>FtpReply.__doFtpCommands</h4>
+<b>__doFtpCommands</b>(<i></i>)
 <p>
-        Private slot to process data from the FTP server.
-</p><a NAME="FtpReply.__processListInfo" ID="FtpReply.__processListInfo"></a>
-<h4>FtpReply.__processListInfo</h4>
-<b>__processListInfo</b>(<i>urlInfo</i>)
+        Private slot doing the sequence of FTP commands to get the requested result.
+</p><a NAME="FtpReply.__retrCallback" ID="FtpReply.__retrCallback"></a>
+<h4>FtpReply.__retrCallback</h4>
+<b>__retrCallback</b>(<i>data</i>)
 <p>
-        Private slot to process list information from the FTP server.
+        Private slot handling the reception of data.
 </p><dl>
-<dt><i>urlInfo</i></dt>
+<dt><i>data</i></dt>
 <dd>
-reference to the information object (QUrlInfo)
+data received from the FTP server (bytes)
 </dd>
 </dl><a NAME="FtpReply.__setContent" ID="FtpReply.__setContent"></a>
 <h4>FtpReply.__setContent</h4>
--- 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