WebBrowser/SafeBrowsing/SafeBrowsingManager.py

branch
safe_browsing
changeset 5820
b610cb5b501a
child 5821
6c7766cde4c1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SafeBrowsing/SafeBrowsingManager.py	Sat Jul 29 19:41:16 2017 +0200
@@ -0,0 +1,201 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2017 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the interface for Google Safe Browsing.
+"""
+
+#
+# Some part of this code were ported from gglsbl.client and adapted
+# to Qt.
+#
+# https://github.com/afilipovich/gglsbl
+#
+
+from __future__ import unicode_literals
+
+import os
+import base64
+
+from PyQt5.QtCore import QObject
+
+import Preferences
+import Utilities
+
+from .SafeBrowsingAPIClient import SafeBrowsingAPIClient
+from .SafeBrowsingCache import SafeBrowsingCache, ThreatList, HashPrefixList
+
+
+class SafeBrowsingManager(QObject):
+    """
+    Class implementing the interface for Google Safe Browsing.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(SafeBrowsingManager, self).__init__()
+        
+        self.__apiKey = Preferences.getWebBrowser("SafeBrowsingApiKey")
+        if self.__apiKey:
+##            self.__apiClient = SafeBrowsingAPIClient(self.__apiKey,
+##                                                     parent=self)
+            # TODO: switch these after debugging is finished
+            self.__apiClient = SafeBrowsingAPIClient(self.__apiKey,
+                                                     parent=self,
+                                                     fairUse=False)
+        else:
+            self.__apiClient = None
+        
+        self.__enabled = (
+            Preferences.getWebBrowser("SafeBrowsingEnabled") and
+            bool(self.__apiKey))
+        
+        gsbCachePath = os.path.join(
+            Utilities.getConfigDir(), "web_browser", "safe_browsing")
+        self.__cache = SafeBrowsingCache(gsbCachePath, self)
+        
+        self.__gsbDialog = None
+        self.__platforms = None     # TODO: delete if not needed
+    
+    def configurationChanged(self):
+        """
+        Public method to handle changes of the settings.
+        """
+        apiKey = Preferences.getWebBrowser("SafeBrowsingApiKey")
+        if apiKey != self.__apiKey:
+            self.__apiKey = apiKey
+            if self.__apiKey:
+                if self.__apiClient:
+                    self.__apiClient.setApiKey(self.__apiKey)
+                else:
+##                    self.__apiClient = SafeBrowsingAPIClient(self.__apiKey,
+##                                                             parent=self)
+                    # TODO: switch these after debugging is finished
+                    self.__apiClient = SafeBrowsingAPIClient(self.__apiKey,
+                                                             parent=self,
+                                                             fairUse=False)
+        
+        self.__enabled = (
+            Preferences.getWebBrowser("SafeBrowsingEnabled") and
+            bool(self.__apiKey))
+    
+    def isEnabled(self):
+        """
+        Public method to check, if safe browsing is enabled.
+        
+        @return flag indicating the enabled state
+        @rtype bool
+        """
+        return self.__enabled
+    
+    def close(self):
+        """
+        Public method to close the safe browsing interface.
+        """
+        self.__cache.close()
+    
+    def fairUseDelayExpired(self):
+        """
+        Public method to check, if the fair use wait period has expired.
+        
+        @return flag indicating expiration
+        @rtype bool
+        """
+        return self.__enabled and self.__apiClient.fairUseDelayExpired()
+    
+    def updateHashPrefixCache(self):
+        """
+        Public method to load or update the locally cached threat lists.
+        
+        @return flag indicating success and an error message
+        @rtype tuple of (bool, str)
+        """
+        if not self.__enabled:
+            return False, self.tr("Safe Browsing is disabled.")
+        
+        if not self.__apiClient.fairUseDelayExpired():
+            return False, \
+                self.tr("The fair use wait period has not expired yet.")
+        
+        # step 1: remove expired hashes
+        self.__cache.cleanupFullHashes()
+        
+        # step 2: update threat lists
+        threatListsForRemove = {}
+        for threatList, clientState in self.__cache.getThreatLists():
+            threatListsForRemove[repr(threatList)] = threatList
+        threatLists = self.__apiClient.getThreatLists()
+        for entry in threatLists:
+            threatList = ThreatList.fromApiEntry(entry)
+            if self.__platforms is None or \
+                    threatList.platformType in self.__platforms:
+                self.__cache.addThreatList(threatList)
+                key = repr(threatList)
+                if key in threatListsForRemove:
+                    del threatListsForRemove[key]
+        for threatList in threatListsForRemove.values():
+            self.__cache.deleteHashPrefixList(threatList)
+            self.__cache.deleteThreatList(threatList)
+        del threatListsForRemove
+        
+        # step 3: update threats
+        threatLists = self.__cache.getThreatLists()
+        clientStates = {}
+        for threatList, clientState in threatLists:
+            clientStates[threatList.asTuple()] = clientState
+        threatsUpdateResponses = \
+            self.__apiClient.getThreatsUpdate(clientStates)
+        for response in threatsUpdateResponses:
+            responseThreatList = ThreatList.fromApiEntry(response)
+            if response["responseType"] == "FULL_UPDATE":
+                self.__cache.deleteHashPrefixList(responseThreatList)
+            for removal in response.get("removals", []):
+                self.__cache.removeHashPrefixIndices(
+                    responseThreatList, removal["rawIndices"]["indices"])
+            for addition in response.get("additions", []):
+                hashPrefixList = HashPrefixList(
+                    addition["rawHashes"]["prefixSize"],
+                    base64.b64decode(addition["rawHashes"]["rawHashes"]))
+                self.__cache.populateHashPrefixList(responseThreatList,
+                                                    hashPrefixList)
+            expectedChecksum = base64.b64decode(response["checksum"]["sha256"])
+            if self.__verifyThreatListChecksum(responseThreatList,
+                                               expectedChecksum):
+                self.__cache.updateThreatListClientState(
+                    responseThreatList, response["newClientState"])
+            else:
+                return False, \
+                    self.tr("Local cache checksum does not match the server."
+                            " Consider cleaning the cache. Threat update has"
+                            " been aborted.")
+        
+        return True, ""
+    
+    def __verifyThreatListChecksum(self, threatList, remoteChecksum):
+        """
+        Private method to verify the local checksum of a threat list with the
+        checksum of the safe browsing server.
+        """
+        localChecksum = self.__cache.hashPrefixListChecksum(threatList)
+        return remoteChecksum == localChecksum
+    
+    def fullCacheCleanup(self):
+        """
+        Public method to clean up the cache completely.
+        """
+        self.__cache.prepareCacheDb()
+    
+    def showSafeBrowsingDialog(self):
+        """
+        Public slot to show the safe browsing management dialog.
+        """
+        if self.__gsbDialog is None:
+            from WebBrowser.WebBrowserWindow import WebBrowserWindow
+            from .SafeBrowsingDialog import SafeBrowsingDialog
+            self.__gsbDialog = SafeBrowsingDialog(
+                self, parent=WebBrowserWindow.mainWindow())
+        
+        self.__gsbDialog.show()

eric ide

mercurial