WebBrowser/SafeBrowsing/SafeBrowsingAPIClient.py

changeset 6233
a64b986abb54
parent 6230
77a1b22c8a1f
child 6234
fb1f9e681848
diff -r d2eac9616a92 -r a64b986abb54 WebBrowser/SafeBrowsing/SafeBrowsingAPIClient.py
--- a/WebBrowser/SafeBrowsing/SafeBrowsingAPIClient.py	Tue Apr 10 19:43:45 2018 +0200
+++ b/WebBrowser/SafeBrowsing/SafeBrowsingAPIClient.py	Wed Apr 11 19:57:23 2018 +0200
@@ -22,6 +22,8 @@
 
 from WebBrowser.WebBrowserWindow import WebBrowserWindow
 
+from .SafeBrowsingThreatList import ThreatList
+
 
 class SafeBrowsingAPIClient(QObject):
     """
@@ -30,7 +32,7 @@
     @signal networkError(str) emitted to indicate a network error
     """
     ClientId = "eric6_API_client"
-    ClientVersion = "1.0.0"
+    ClientVersion = "2.0.0"
     
     GsbUrlTemplate = "https://safebrowsing.googleapis.com/v4/{0}?key={1}"
     
@@ -47,11 +49,20 @@
         @param parent reference to the parent object
         @type QObject
         """
+        super(SafeBrowsingAPIClient, self).__init__(parent)
+        
         self.__apiKey = apiKey
         self.__fairUse = fairUse
         
         self.__nextRequestNoSoonerThan = QDateTime()
         self.__failCount = 0
+        
+        self.__lookupApiCache = {}
+        # Temporary cache used by the lookup API (v4)
+        # key: URL as string
+        # value: dictionary with these entries:
+        #   "validUntil": (QDateTime)
+        #   "threatInfo": (ThreatList)
     
     def setApiKey(self, apiKey):
         """
@@ -262,7 +273,89 @@
     ## Methods below implement the 'Lookup API (v4)'
     #######################################################################
     
-    # TODO: implement the Lookup API (including temporary caching)
+    def lookupUrl(self, url, platforms):
+        """
+        Public method to send an URL to Google for checking.
+        
+        @param url URL to be checked
+        @type QUrl
+        @param platforms list of platform types to check against
+        @type list of str
+        @return list of threat list info objects
+        @rtype list of ThreatList
+        """
+        # sanitize the URL by removing user info and query data
+        url = url.adjusted(
+            QUrl.RemoveUserInfo | QUrl.RemoveQuery | QUrl.RemoveFragment
+        )
+        urlStr = url.toString()
+        if urlStr in self.__lookupApiCache:
+            if self.__lookupApiCache[urlStr]["validUntil"] > \
+               QDateTime.currentDateTime():
+                # cached entry is still valid
+                return self.__lookupApiCache[urlStr]["threatInfo"]
+            else:
+                del self.__lookupApiCache[urlStr]
+        
+        requestBody = {
+            "client": {
+                "clientId": self.ClientId,
+                "clientVersion": self.ClientVersion,
+            },
+            "threatInfo": {
+                "threatTypes": [
+                    "MALWARE", "SOCIAL_ENGINEERING", "UNWANTED_SOFTWARE",
+                    "POTENTIALLY_HARMFUL_APPLICATION",
+                ],
+                "platformTypes": platforms,
+                "threatEntryTypes": ["URL", "EXECUTABLE"],
+                "threatEntries": [
+                    {"url": urlStr},
+                ],
+            },
+        }
+        
+        data = QByteArray(json.dumps(requestBody).encode("utf-8"))
+        url = QUrl(self.GsbUrlTemplate.format("threatMatches:find",
+                                              self.__apiKey))
+        req = QNetworkRequest(url)
+        req.setHeader(QNetworkRequest.ContentTypeHeader, "application/json")
+        reply = WebBrowserWindow.networkManager().post(req, data)
+        
+        while reply.isRunning():
+            QCoreApplication.processEvents(QEventLoop.AllEvents, 200)
+            # max. 200 ms processing
+        
+        threats = []
+        if reply.error() != QNetworkReply.NoError:
+            self.networkError.emit(reply.errorString())
+        else:
+            res = json.loads(str(reply.readAll(), "utf-8"))
+            if res and "matches" in res:
+                cacheDuration = 0
+                for match in res["matches"]:
+                    threatInfo = ThreatList(
+                        match["threatType"],
+                        match["platformType"],
+                        match["threatEntryType"],
+                    )
+                    threats.append(threatInfo)
+                    if "cacheDuration" in match:
+                        cacheDurationSec = int(
+                            match["cacheDuration"].strip().rstrip("s")
+                            .split(".")[0])
+                        if cacheDurationSec > cacheDuration:
+                            cacheDuration = cacheDurationSec
+                if cacheDuration > 0 and bool(threats):
+                    validUntil = QDateTime.currentDateTime().addSecs(
+                        cacheDuration)
+                    self.__lookupApiCache[urlStr] = {
+                        "validUntil": validUntil,
+                        "threatInfo": threats
+                    }
+        
+        reply.deleteLater()
+        return threats
     
     #######################################################################
     ## Methods below implement global (class wide) functionality

eric ide

mercurial