Started implementing the SafeBrowsingUrl class. safe_browsing

Sun, 16 Jul 2017 19:34:54 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 16 Jul 2017 19:34:54 +0200
branch
safe_browsing
changeset 5808
7bf90dcae4e1
parent 5807
d2eb934fa6b4
child 5809
5b53c17b7d93

Started implementing the SafeBrowsingUrl class.

Preferences/ConfigurationPages/WebBrowserPage.py file | annotate | diff | comparison | revisions
Preferences/ConfigurationPages/WebBrowserPage.ui file | annotate | diff | comparison | revisions
Preferences/__init__.py file | annotate | diff | comparison | revisions
WebBrowser/SafeBrowsing/SafeBrowsingUrl.py file | annotate | diff | comparison | revisions
WebBrowser/SafeBrowsing/__init__.py file | annotate | diff | comparison | revisions
eric6.e4p file | annotate | diff | comparison | revisions
--- a/Preferences/ConfigurationPages/WebBrowserPage.py	Sun Jul 16 17:00:01 2017 +0200
+++ b/Preferences/ConfigurationPages/WebBrowserPage.py	Sun Jul 16 19:34:54 2017 +0200
@@ -49,6 +49,8 @@
         defaultSchemes = ["file://", "http://", "https://"]
         self.defaultSchemeCombo.addItems(defaultSchemes)
         
+        self.__gsbHelpDialog = None
+        
         # set initial values
         self.singleHelpWindowCheckBox.setChecked(
             Preferences.getWebBrowser("SingleWebBrowserWindow"))
@@ -173,6 +175,10 @@
                 Preferences.getWebBrowser("AllowRunningInsecureContent"))
         except KeyError:
             self.insecureContentsCheckBox.setEnabled(False)
+        self.gsbGroupBox.setChecked(
+            Preferences.getWebBrowser("SafeBrowsingEnabled"))
+        self.gsbApiKeyEdit.setText(
+            Preferences.getWebBrowser("SafeBrowsingApiKey"))
         
         try:
             # Qt 5.8
@@ -319,6 +325,12 @@
             Preferences.setWebBrowser(
                 "AllowRunningInsecureContent",
                 self.insecureContentsCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "SafeBrowsingEnabled",
+            self.gsbGroupBox.isChecked())
+        Preferences.setWebBrowser(
+            "SafeBrowsingApiKey",
+            self.gsbApiKeyEdit.text())
         
         if self.printBackgroundCheckBox.isEnabled():
             Preferences.setWebBrowser(
@@ -381,6 +393,23 @@
         from WebBrowser.Network.SendRefererWhitelistDialog import \
             SendRefererWhitelistDialog
         SendRefererWhitelistDialog(self).exec_()
+    
+    @pyqtSlot()
+    def on_gsbHelpButton_clicked(self):
+        """
+        Private slot to show some help text "How to create a safe
+        browsing API key.".
+        """
+        if self.__gsbHelpDialog is None:
+            from E5Gui.E5SimpleHelpDialog import E5SimpleHelpDialog
+            from WebBrowser.SafeBrowsing import SafeBrowsingHelp
+            
+            helpStr = SafeBrowsingHelp()
+            self.__gsbHelpDialog = E5SimpleHelpDialog(
+                title=self.tr("Google Safe Browsing API Help"),
+                helpStr=helpStr, parent=self)
+        
+        self.__gsbHelpDialog.show()
 
 
 def create(dlg):
--- a/Preferences/ConfigurationPages/WebBrowserPage.ui	Sun Jul 16 17:00:01 2017 +0200
+++ b/Preferences/ConfigurationPages/WebBrowserPage.ui	Sun Jul 16 19:34:54 2017 +0200
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>616</width>
-    <height>1610</height>
+    <height>2000</height>
    </rect>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout_2">
@@ -479,8 +479,8 @@
      <property name="title">
       <string>Security</string>
      </property>
-     <layout class="QHBoxLayout" name="horizontalLayout_7">
-      <item>
+     <layout class="QGridLayout" name="gridLayout_9">
+      <item row="0" column="0">
        <widget class="QCheckBox" name="xssAuditingCheckBox">
         <property name="toolTip">
          <string>Select to enable XSS auditing</string>
@@ -494,7 +494,7 @@
         </property>
        </widget>
       </item>
-      <item>
+      <item row="0" column="1">
        <widget class="QCheckBox" name="insecureContentsCheckBox">
         <property name="toolTip">
          <string>Select to allow HTTPS pages to run JavaScript, CSS, plugins or web-sockets from HTTP URLs</string>
@@ -504,6 +504,45 @@
         </property>
        </widget>
       </item>
+      <item row="1" column="0" colspan="2">
+       <widget class="QGroupBox" name="gsbGroupBox">
+        <property name="toolTip">
+         <string>Select to enable the Google sage browsing support</string>
+        </property>
+        <property name="title">
+         <string>Google Safe Browsing</string>
+        </property>
+        <property name="checkable">
+         <bool>true</bool>
+        </property>
+        <layout class="QGridLayout" name="gridLayout_7">
+         <item row="0" column="0">
+          <widget class="QLabel" name="label_14">
+           <property name="text">
+            <string>API Key:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QLineEdit" name="gsbApiKeyEdit">
+           <property name="toolTip">
+            <string>Enter the Google Safe Browsing API key</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0" colspan="2">
+          <widget class="QPushButton" name="gsbHelpButton">
+           <property name="toolTip">
+            <string>Press to get some help about obtaining the API key</string>
+           </property>
+           <property name="text">
+            <string>Google Safe Browsing API Help</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </item>
      </layout>
     </widget>
    </item>
@@ -948,6 +987,9 @@
   <tabstop>refererWhitelistButton</tabstop>
   <tabstop>xssAuditingCheckBox</tabstop>
   <tabstop>insecureContentsCheckBox</tabstop>
+  <tabstop>gsbGroupBox</tabstop>
+  <tabstop>gsbApiKeyEdit</tabstop>
+  <tabstop>gsbHelpButton</tabstop>
   <tabstop>expireHistory</tabstop>
   <tabstop>diskCacheCheckBox</tabstop>
   <tabstop>cacheSizeSpinBox</tabstop>
--- a/Preferences/__init__.py	Sun Jul 16 17:00:01 2017 +0200
+++ b/Preferences/__init__.py	Sun Jul 16 19:34:54 2017 +0200
@@ -1119,6 +1119,9 @@
         "SessionAutoSave": True,
         "SessionAutoSaveInterval": 15,  # interval in seconds
         "SessionLastActivePath": "",
+        # Google Safe Browsing
+        "SafeBrowsingEnabled": True,
+        "SafeBrowsingApiKey": "",       # API key
     }
     if QWebEngineSettings:
         webBrowserDefaults["HelpViewerType"] = 1      # eric browser
@@ -2919,6 +2922,7 @@
                  "SpellCheckEnabled", "ShowToolbars", "MenuBarVisible",
                  "BookmarksToolBarVisible", "StatusBarVisible",
                  "SessionAutoSave", "LoadTabOnActivation",
+                 "SafeBrowsingEnabled",
                  ]:
         return toBool(prefClass.settings.value(
             "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SafeBrowsing/SafeBrowsingUrl.py	Sun Jul 16 19:34:54 2017 +0200
@@ -0,0 +1,134 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2017 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an URL representation suitable for Google Safe Browsing.
+"""
+
+from __future__ import unicode_literals
+
+try:
+    import urlparse     # Py2
+    import urllib       # Py2
+except ImportError:
+    import urllib.parse as urllib
+    from urllib import parse as urlparse
+
+import re
+import posixpath
+import socket
+import struct
+
+import Preferences
+
+
+class SafeBrowsingUrl(object):
+    """
+    Class implementing an URL representation suitable for Google Safe Browsing.
+    """
+    #
+    # Modeled after the URL class of the gglsbl package.
+    # https://github.com/afilipovich/gglsbl
+    #
+    def __init__(self, url):
+        """
+        Constructor
+        
+        @param url URL to be embedded
+        @type str
+        """
+        self.__url = url
+    
+    def hashes(self):
+        """
+        Public method to get the hashes of all possible permutations of the URL
+        in canonical form.
+        
+        @return generator for the URL hashes
+        @rtype generator of str
+        """
+        for variant in self.permutations(self.canonical()):
+            urlHash = self.digest(variant)
+            yield urlHash
+    
+    def canonical(self):
+        """
+        Public method to convert the URL to the canonical form.
+        
+        @return canonical form of the URL
+        @rtype str
+        """
+        def fullUnescape(u):
+            """
+            Method to recursively unescape an URL.
+            
+            @param u URL string to unescape
+            @type str
+            @return unescaped URL string
+            @rtype str
+            """
+            uu = urllib.unquote(u)
+            if uu == u:
+                return uu
+            else:
+                return fullUnescape(uu)
+        
+        def quote(s):
+            """
+            Method to quote a string.
+            
+            @param string to be quoted
+            @type str
+            @return quoted string
+            @rtype str
+            """
+            safeChars = '!"$&\'()*+,-./:;<=>?@[\\]^_`{|}~'
+            return urllib.quote(s, safe=safeChars)
+        
+        url = self.__url.strip()
+        url = url.replace('\n', '').replace('\r', '').replace('\t', '')
+        url = url.split('#', 1)[0]
+        if url.startswith('//'):
+            url = Preferences.getWebBrowser("DefaultScheme")[:-3] + url
+        if len(url.split('://')) <= 1:
+            url = Preferences.getWebBrowser("DefaultScheme") + url
+        url = quote(fullUnescape(url))
+        urlParts = urlparse.urlsplit(url)
+        if not urlParts[0]:
+            url = Preferences.getWebBrowser("DefaultScheme") + url
+            urlParts = urlparse.urlsplit(url)
+        protocol = urlParts.scheme
+        host = fullUnescape(urlParts.hostname)
+        path = fullUnescape(urlParts.path)
+        query = urlParts.query
+        if not query and '?' not in url:
+            query = None
+        if not path:
+            path = '/'
+        hasTrailingSlash = (path[-1] == '/')
+        path = posixpath.normpath(path).replace('//', '/')
+        if hasTrailingSlash and path[-1] != '/':
+            path += '/'
+        port = urlParts.port
+        host = host.strip('.')
+        host = re.sub(r'\.+', '.', host).lower()
+        if host.isdigit():
+            try:
+                host = socket.inet_ntoa(struct.pack("!I", int(host)))
+            except Exception:
+                pass
+        if host.startswith('0x') and '.' not in host:
+            try:
+                host = socket.inet_ntoa(struct.pack("!I", int(host, 16)))
+            except Exception:
+                pass
+        quotedPath = quote(path)
+        quotedHost = quote(host)
+        if port is not None:
+            quotedHost = '{0}:{1}'.format(quotedHost, port)
+        canonicalUrl = '{0}://{1}{2}'.format(protocol, quotedHost, quotedPath)
+        if query is not None:
+            canonicalUrl = '{0}?{1}'.format(canonicalUrl, query)
+        return canonicalUrl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SafeBrowsing/__init__.py	Sun Jul 16 19:34:54 2017 +0200
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2017 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the safe browsing functionality.
+"""
+
+from __future__ import unicode_literals
+
+
+def SafeBrowsingHelp():
+    """
+    Module function to get some help about how to enable the Google Mail
+    OAuth2 service.
+    
+    @return help text
+    @rtype str
+    """
+    return (
+        "<h2>Steps to get a Google Safe Browsing API key</h2>"
+        "<p>In order to use Google Safe Browsing you need a Google Account,"
+        " a Google Developer Console project, and an API key. You also need"
+        " to activate the Safe Browsing APIs for use with your project.</p>"
+        "<ol>"
+        "<li>Google Account<br/>You need a Google Account in order to create"
+        " a project. If you don't already have an account, sign up at"
+        " <a href='https://accounts.google.com/SignUp'>Create your Google"
+        " Account</a>.</li>"
+        "<li>Developer Console Project<br/>You need a Google Developer Console"
+        " project in order to create an API key. If you don't already have a"
+        " project, see"
+        " <a href='https://support.google.com/cloud/answer/6251787?hl=en'>"
+        "Create, shut down, and restore projects</a>.</li>"
+        "<li>API Key<br/>You need an API key to access the Safe Browsing APIs."
+        " An API key authenticates you as an API user and allows you to"
+        " interact with the APIs. To set up an API key, see "
+        "<a href='https://support.google.com/cloud/answer/6158862"
+        "?hl=en&ref_topic=6262490'>Setting up API keys</a>. Your new API key"
+        " appears in a table. Copy and paste this key into the line edit above"
+        " the button used to show this help.</li>"
+        "<li>Activate the API<br/>Finally, you need to activate the Safe"
+        " Browsing API for use with your project. To learn how to do this, see"
+        " <a href='https://support.google.com/cloud/answer/6158841?hl=en'>"
+        "Activate and deactivate APIs</a>. The API to enable is referred to as"
+        " &quot;Google Safe Browsing API&quot;.</li>"
+        "</ol>"
+    )
--- a/eric6.e4p	Sun Jul 16 17:00:01 2017 +0200
+++ b/eric6.e4p	Sun Jul 16 19:34:54 2017 +0200
@@ -1404,6 +1404,8 @@
     <Source>WebBrowser/QtHelp/QtHelpDocumentationSelectionDialog.py</Source>
     <Source>WebBrowser/QtHelp/QtHelpFiltersDialog.py</Source>
     <Source>WebBrowser/QtHelp/__init__.py</Source>
+    <Source>WebBrowser/SafeBrowsing/SafeBrowsingUrl.py</Source>
+    <Source>WebBrowser/SafeBrowsing/__init__.py</Source>
     <Source>WebBrowser/SearchWidget.py</Source>
     <Source>WebBrowser/Session/SessionManager.py</Source>
     <Source>WebBrowser/Session/SessionManagerDialog.py</Source>

eric ide

mercurial