Sun, 16 Jul 2017 19:34:54 +0200
Started implementing the SafeBrowsingUrl class.
--- 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" + " "Google Safe Browsing API".</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>