WebBrowser/SafeBrowsing/SafeBrowsingAPIClient.py

branch
safe_browsing
changeset 5809
5b53c17b7d93
child 5811
5358a3c7995f
diff -r 7bf90dcae4e1 -r 5b53c17b7d93 WebBrowser/SafeBrowsing/SafeBrowsingAPIClient.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SafeBrowsing/SafeBrowsingAPIClient.py	Mon Jul 17 19:58:37 2017 +0200
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2017 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the low level interface for Google Safe Browsing.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+import json
+import random
+
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QDateTime, QTimer, \
+    QUrl
+from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+
+class SafeBrowsingAPIClient(QObject):
+    """
+    Class implementing the low level interface for Google Safe Browsing.
+    """
+    ClientId = "eric6_API_client"
+    ClientVersion = "1.0.0"
+    
+    GsbUrlTemplate = "https://safebrowsing.googleapis.com/v4/{0}?key={1}"
+    
+    networkError = pyqtSignal(str)
+    threatLists = pyqtSignal(list)
+    
+    # threatListUpdates:fetch   Content-Type: application/json      POST
+    # fullHashes:find           Content-Type: application/json      POST
+    
+    def __init__(self, apiKey, fairUse=True, parent=None):
+        """
+        Constructor
+        
+        @param apiKey API key to be used
+        @type str
+        @param fairUse flag indicating to follow the fair use policy
+        @type bool
+        @param parent reference to the parent object
+        @type QObject
+        """
+        self.__apiKey = apiKey
+        self.__fairUse = fairUse
+        
+        self.__nextRequestNoSoonerThan = QDateTime()
+        self.__replies = []
+        self.__failCount = 0
+    
+    def getThreatLists(self):
+        """
+        Public method to retrieve all available threat lists.
+        
+        @return threat lists
+        @rtype list of dictionaries
+        """
+        url = QUrl(self.GsbUrlTemplate.format("threatLists", self.__apiKey))
+        req = QNetworkRequest(url)
+        reply = WebBrowserWindow.networkManager().get(req)
+        reply.finished.connect(self.__threatListsReceived)
+    
+    @pyqtSlot()
+    def __threatListsReceived(self):
+        """
+        Private slot handling the threat lists.
+        """
+        reply = self.sender()
+        result, hasError = self.__extractData(reply)
+        if hasError:
+            # reschedule
+            self.networkError.emit(reply.errorString())
+            self.__reschedule(reply.error(), self.getThreatLists)
+        else:
+            self.__setWaitDuration(result.get("minimumWaitDuration"))
+            self.threatLists.emit(result["threatLists"])
+            self.__failCount = 0
+        
+        if reply in self.__replies:
+            self.__replies.remove(reply)
+        reply.deleteLater()
+    
+    def __extractData(self, reply):
+        """
+        Private method to extract the data of a network reply.
+        
+        @param reply reference to the network reply object
+        @type QNetworkReply
+        @return tuple containing the extracted data and an error flag
+        @type tuple of (list or dict, bool)
+        """
+        if reply.error() != QNetworkReply.NoError:
+            return None, True
+        
+        result = json.loads(str(reply.readAll(), "utf-8"))
+        return result, False
+    
+    def __setWaitDuration(self, minimumWaitDuration):
+        """
+        Private method to set the minimum wait duration.
+        
+        @param minimumWaitDuration duration to be set
+        @type str
+        """
+        if not self.__fairUse or minimumWaitDuration is None:
+            self.__nextRequestNoSoonerThan = QDateTime()
+        else:
+            waitDuration = int(minimumWaitDuration.rstrip("s"))
+            self.__nextRequestNoSoonerThan = \
+                QDateTime.currentDateTime().addSecs(waitDuration)
+    
+    def __reschedule(self, errorCode, func):
+        """
+        Private method to reschedule an API access.
+        
+        @param errorCode error code returned by the function to be rescheduled
+        @type int
+        @param func function to be rescheduled
+        @type func
+        """
+        if errorCode >= 500:
+            return
+        
+        self.__failCount += 1
+        waitDuration = min(
+            int(2 ** (self.__failCount - 1) * 15 * 60 * (1 + random.random())),
+            24 * 60 * 60)
+        QTimer.singleShot(waitDuration * 1000, func)

eric ide

mercurial