We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Handle "::" in URLs specially
Split from #542. /cc @flvi0

> What exactly are those :: links anyways, is that a standard URI form for external applications? Maybe we can parse/qualify it somehow differently to make searching for scoped C++ symbols work as expected?

[QUrl](http://doc.qt.io/qt-5/qurl.html) parses `foo::bar` as a valid URL with `foo` as scheme and `:bar` as path. I'm not sure if this is something sensible to do according to the URL standard. I guess `qutebrowser.urlutils.is_url` should handle this as a special case, and maybe a Qt bug should be submitted if this is invalid according to [RFC3986](https://tools.ietf.org/html/rfc3986) and the [WHATWG living URL standard](https://url.spec.whatwg.org/)?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `qutebrowser/utils/urlutils.py`
Content:
```
1 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
2 
3 # Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
4 #
5 # This file is part of qutebrowser.
6 #
7 # qutebrowser is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # qutebrowser is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.
19 
20 """Utils regarding URL handling."""
21 
22 import re
23 import os.path
24 import ipaddress
25 import posixpath
26 import urllib.parse
27 
28 from PyQt5.QtCore import QUrl
29 from PyQt5.QtNetwork import QHostInfo, QHostAddress
30 
31 from qutebrowser.config import config, configexc
32 from qutebrowser.utils import log, qtutils, message
33 from qutebrowser.commands import cmdexc
34 
35 
36 # FIXME: we probably could raise some exceptions on invalid URLs
37 # https://github.com/The-Compiler/qutebrowser/issues/108
38 
39 
40 def _parse_search_term(s):
41     """Get a search engine name and search term from a string.
42 
43     Args:
44         s: The string to get a search engine for.
45 
46     Return:
47         A (engine, term) tuple, where engine is None for the default engine.
48     """
49     m = re.search(r'(^\w+)\s+(.+)($|\s+)', s)
50     if m:
51         engine = m.group(1)
52         try:
53             config.get('searchengines', engine)
54         except configexc.NoOptionError:
55             engine = None
56             term = s
57         else:
58             term = m.group(2).rstrip()
59     else:
60         engine = None
61         term = s
62     log.url.debug("engine {}, term '{}'".format(engine, term))
63     return (engine, term)
64 
65 
66 def _get_search_url(txt):
67     """Get a search engine URL for a text.
68 
69     Args:
70         txt: Text to search for.
71 
72     Return:
73         The search URL as a QUrl.
74     """
75     log.url.debug("Finding search engine for '{}'".format(txt))
76     engine, term = _parse_search_term(txt)
77     if not term:
78         raise FuzzyUrlError("No search term given")
79     if engine is None:
80         template = config.get('searchengines', 'DEFAULT')
81     else:
82         template = config.get('searchengines', engine)
83     url = qurl_from_user_input(template.format(urllib.parse.quote(term)))
84     qtutils.ensure_valid(url)
85     return url
86 
87 
88 def _is_url_naive(urlstr):
89     """Naive check if given URL is really a URL.
90 
91     Args:
92         urlstr: The URL to check for, as string.
93 
94     Return:
95         True if the URL really is a URL, False otherwise.
96     """
97     url = qurl_from_user_input(urlstr)
98     try:
99         ipaddress.ip_address(urlstr)
100     except ValueError:
101         pass
102     else:
103         # Valid IPv4/IPv6 address
104         return True
105 
106     # Qt treats things like "23.42" or "1337" or "0xDEAD" as valid URLs
107     # which we don't want to. Note we already filtered *real* valid IPs
108     # above.
109     if not QHostAddress(urlstr).isNull():
110         return False
111 
112     if not url.isValid():
113         return False
114     elif '.' in url.host():
115         return True
116     elif url.host() == 'localhost':
117         return True
118     else:
119         return False
120 
121 
122 def _is_url_dns(url):
123     """Check if a URL is really a URL via DNS.
124 
125     Args:
126         url: The URL to check for as QUrl, ideally via qurl_from_user_input.
127 
128     Return:
129         True if the URL really is a URL, False otherwise.
130     """
131     if not url.isValid():
132         return False
133     host = url.host()
134     log.url.debug("DNS request for {}".format(host))
135     if not host:
136         return False
137     info = QHostInfo.fromName(host)
138     return not info.error()
139 
140 
141 def fuzzy_url(urlstr, cwd=None, relative=False, do_search=True):
142     """Get a QUrl based on an user input which is URL or search term.
143 
144     Args:
145         urlstr: URL to load as a string.
146         cwd: The current working directory, or None.
147         relative: Whether to resolve relative files.
148         do_search: Whether to perform a search on non-URLs.
149 
150     Return:
151         A target QUrl to a searchpage or the original URL.
152     """
153     expanded = os.path.expanduser(urlstr)
154     if relative and cwd:
155         path = os.path.join(cwd, expanded)
156     elif relative:
157         try:
158             path = os.path.abspath(expanded)
159         except OSError:
160             path = None
161     elif os.path.isabs(expanded):
162         path = expanded
163     else:
164         path = None
165 
166     stripped = urlstr.strip()
167     if path is not None and os.path.exists(path):
168         log.url.debug("URL is a local file")
169         url = QUrl.fromLocalFile(path)
170     elif (not do_search) or is_url(stripped):
171         # probably an address
172         log.url.debug("URL is a fuzzy address")
173         url = qurl_from_user_input(urlstr)
174     else:  # probably a search term
175         log.url.debug("URL is a fuzzy search term")
176         try:
177             url = _get_search_url(urlstr)
178         except ValueError:  # invalid search engine
179             url = qurl_from_user_input(stripped)
180     log.url.debug("Converting fuzzy term {} to URL -> {}".format(
181                   urlstr, url.toDisplayString()))
182     if do_search and config.get('general', 'auto-search'):
183         qtutils.ensure_valid(url)
184     else:
185         if not url.isValid():
186             raise FuzzyUrlError("Invalid URL '{}'!".format(urlstr))
187     return url
188 
189 
190 def _has_explicit_scheme(url):
191     """Check if an url has an explicit scheme given.
192 
193     Args:
194         url: The URL as QUrl.
195     """
196     return url.isValid() and url.scheme() and not url.path().startswith(' ')
197 
198 
199 def is_special_url(url):
200     """Return True if url is an about:... or other special URL.
201 
202     Args:
203         url: The URL as QUrl.
204     """
205     if not url.isValid():
206         return False
207     special_schemes = ('about', 'qute', 'file')
208     return url.scheme() in special_schemes
209 
210 
211 def is_url(urlstr):
212     """Check if url seems to be a valid URL.
213 
214     Args:
215         urlstr: The URL as string.
216 
217     Return:
218         True if it is a valid URL, False otherwise.
219     """
220     autosearch = config.get('general', 'auto-search')
221 
222     log.url.debug("Checking if '{}' is a URL (autosearch={}).".format(
223                   urlstr, autosearch))
224 
225     urlstr = urlstr.strip()
226     qurl = QUrl(urlstr)
227 
228     if not autosearch:
229         # no autosearch, so everything is a URL unless it has an explicit
230         # search engine.
231         engine, _term = _parse_search_term(urlstr)
232         if engine is None:
233             return True
234         else:
235             return False
236 
237     if _has_explicit_scheme(qurl):
238         # URLs with explicit schemes are always URLs
239         log.url.debug("Contains explicit scheme")
240         url = True
241     elif ' ' in urlstr:
242         # A URL will never contain a space
243         log.url.debug("Contains space -> no URL")
244         url = False
245     elif is_special_url(qurl):
246         # Special URLs are always URLs, even with autosearch=False
247         log.url.debug("Is an special URL.")
248         url = True
249     elif autosearch == 'dns':
250         log.url.debug("Checking via DNS")
251         # We want to use qurl_from_user_input here, as the user might enter
252         # "foo.de" and that should be treated as URL here.
253         url = _is_url_dns(qurl_from_user_input(urlstr))
254     elif autosearch == 'naive':
255         log.url.debug("Checking via naive check")
256         url = _is_url_naive(urlstr)
257     else:
258         raise ValueError("Invalid autosearch value")
259     return url and qurl_from_user_input(urlstr).isValid()
260 
261 
262 def qurl_from_user_input(urlstr):
263     """Get a QUrl based on an user input. Additionally handles IPv6 addresses.
264 
265     QUrl.fromUserInput handles something like '::1' as a file URL instead of an
266     IPv6, so we first try to handle it as a valid IPv6, and if that fails we
267     use QUrl.fromUserInput.
268 
269     WORKAROUND - https://bugreports.qt-project.org/browse/QTBUG-41089
270     FIXME - Maybe https://codereview.qt-project.org/#/c/93851/ has a better way
271             to solve this?
272     https://github.com/The-Compiler/qutebrowser/issues/109
273 
274     Args:
275         urlstr: The URL as string.
276 
277     Return:
278         The converted QUrl.
279     """
280     # First we try very liberally to separate something like an IPv6 from the
281     # rest (e.g. path info or parameters)
282     match = re.match(r'\[?([0-9a-fA-F:.]+)\]?(.*)', urlstr.strip())
283     if match:
284         ipstr, rest = match.groups()
285     else:
286         ipstr = urlstr.strip()
287         rest = ''
288     # Then we try to parse it as an IPv6, and if we fail use
289     # QUrl.fromUserInput.
290     try:
291         ipaddress.IPv6Address(ipstr)
292     except ipaddress.AddressValueError:
293         return QUrl.fromUserInput(urlstr)
294     else:
295         return QUrl('http://[{}]{}'.format(ipstr, rest))
296 
297 
298 def invalid_url_error(win_id, url, action):
299     """Display an error message for an URL.
300 
301     Args:
302         win_id: The window ID to show the error message in.
303         action: The action which was interrupted by the error.
304     """
305     if url.isValid():
306         raise ValueError("Calling invalid_url_error with valid URL {}".format(
307             url.toDisplayString()))
308     errstring = "Trying to {} with invalid URL".format(action)
309     if url.errorString():
310         errstring += " - {}".format(url.errorString())
311     message.error(win_id, errstring)
312 
313 
314 def raise_cmdexc_if_invalid(url):
315     """Check if the given QUrl is invalid, and if so, raise a CommandError."""
316     if not url.isValid():
317         errstr = "Invalid URL {}".format(url.toDisplayString())
318         url_error = url.errorString()
319         if url_error:
320             errstr += " - {}".format(url_error)
321         raise cmdexc.CommandError(errstr)
322 
323 
324 def filename_from_url(url):
325     """Get a suitable filename from an URL.
326 
327     Args:
328         url: The URL to parse, as a QUrl.
329 
330     Return:
331         The suggested filename as a string, or None.
332     """
333     if not url.isValid():
334         return None
335     pathname = posixpath.basename(url.path())
336     if pathname:
337         return pathname
338     elif url.host():
339         return url.host() + '.html'
340     else:
341         return None
342 
343 
344 def host_tuple(url):
345     """Get a (scheme, host, port) tuple.
346 
347     This is suitable to identify a connection, e.g. for SSL errors.
348     """
349     return (url.scheme(), url.host(), url.port())
350 
351 
352 class FuzzyUrlError(Exception):
353 
354     """Exception raised by fuzzy_url on problems."""
355 
356     pass
357
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py
--- a/qutebrowser/utils/urlutils.py
+++ b/qutebrowser/utils/urlutils.py
@@ -193,7 +193,14 @@
     Args:
         url: The URL as QUrl.
     """
-    return url.isValid() and url.scheme() and not url.path().startswith(' ')
+
+    # Note that generic URI syntax actually would allow a second colon
+    # after the scheme delimiter. Since we don't know of any URIs
+    # using this and want to support e.g. searching for scoped C++
+    # symbols, we treat this as not an URI anyways.
+    return (url.isValid() and url.scheme()
+            and not url.path().startswith(' ')
+            and not url.path().startswith(':'))
 
 
 def is_special_url(url):

{"golden_diff": "diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py\n--- a/qutebrowser/utils/urlutils.py\n+++ b/qutebrowser/utils/urlutils.py\n@@ -193,7 +193,14 @@\n     Args:\n         url: The URL as QUrl.\n     \"\"\"\n-    return url.isValid() and url.scheme() and not url.path().startswith(' ')\n+\n+    # Note that generic URI syntax actually would allow a second colon\n+    # after the scheme delimiter. Since we don't know of any URIs\n+    # using this and want to support e.g. searching for scoped C++\n+    # symbols, we treat this as not an URI anyways.\n+    return (url.isValid() and url.scheme()\n+            and not url.path().startswith(' ')\n+            and not url.path().startswith(':'))\n \n \n def is_special_url(url):\n", "issue": "Handle \"::\" in URLs specially\nSplit from #542. /cc @flvi0\n\n> What exactly are those :: links anyways, is that a standard URI form for external applications? Maybe we can parse/qualify it somehow differently to make searching for scoped C++ symbols work as expected?\n\n[QUrl](http://doc.qt.io/qt-5/qurl.html) parses `foo::bar` as a valid URL with `foo` as scheme and `:bar` as path. I'm not sure if this is something sensible to do according to the URL standard. I guess `qutebrowser.urlutils.is_url` should handle this as a special case, and maybe a Qt bug should be submitted if this is invalid according to [RFC3986](https://tools.ietf.org/html/rfc3986) and the [WHATWG living URL standard](https://url.spec.whatwg.org/)?\n\n", "before_files": [{"content": "# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>\n#\n# This file is part of qutebrowser.\n#\n# qutebrowser is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# qutebrowser is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"Utils regarding URL handling.\"\"\"\n\nimport re\nimport os.path\nimport ipaddress\nimport posixpath\nimport urllib.parse\n\nfrom PyQt5.QtCore import QUrl\nfrom PyQt5.QtNetwork import QHostInfo, QHostAddress\n\nfrom qutebrowser.config import config, configexc\nfrom qutebrowser.utils import log, qtutils, message\nfrom qutebrowser.commands import cmdexc\n\n\n# FIXME: we probably could raise some exceptions on invalid URLs\n# https://github.com/The-Compiler/qutebrowser/issues/108\n\n\ndef _parse_search_term(s):\n    \"\"\"Get a search engine name and search term from a string.\n\n    Args:\n        s: The string to get a search engine for.\n\n    Return:\n        A (engine, term) tuple, where engine is None for the default engine.\n    \"\"\"\n    m = re.search(r'(^\\w+)\\s+(.+)($|\\s+)', s)\n    if m:\n        engine = m.group(1)\n        try:\n            config.get('searchengines', engine)\n        except configexc.NoOptionError:\n            engine = None\n            term = s\n        else:\n            term = m.group(2).rstrip()\n    else:\n        engine = None\n        term = s\n    log.url.debug(\"engine {}, term '{}'\".format(engine, term))\n    return (engine, term)\n\n\ndef _get_search_url(txt):\n    \"\"\"Get a search engine URL for a text.\n\n    Args:\n        txt: Text to search for.\n\n    Return:\n        The search URL as a QUrl.\n    \"\"\"\n    log.url.debug(\"Finding search engine for '{}'\".format(txt))\n    engine, term = _parse_search_term(txt)\n    if not term:\n        raise FuzzyUrlError(\"No search term given\")\n    if engine is None:\n        template = config.get('searchengines', 'DEFAULT')\n    else:\n        template = config.get('searchengines', engine)\n    url = qurl_from_user_input(template.format(urllib.parse.quote(term)))\n    qtutils.ensure_valid(url)\n    return url\n\n\ndef _is_url_naive(urlstr):\n    \"\"\"Naive check if given URL is really a URL.\n\n    Args:\n        urlstr: The URL to check for, as string.\n\n    Return:\n        True if the URL really is a URL, False otherwise.\n    \"\"\"\n    url = qurl_from_user_input(urlstr)\n    try:\n        ipaddress.ip_address(urlstr)\n    except ValueError:\n        pass\n    else:\n        # Valid IPv4/IPv6 address\n        return True\n\n    # Qt treats things like \"23.42\" or \"1337\" or \"0xDEAD\" as valid URLs\n    # which we don't want to. Note we already filtered *real* valid IPs\n    # above.\n    if not QHostAddress(urlstr).isNull():\n        return False\n\n    if not url.isValid():\n        return False\n    elif '.' in url.host():\n        return True\n    elif url.host() == 'localhost':\n        return True\n    else:\n        return False\n\n\ndef _is_url_dns(url):\n    \"\"\"Check if a URL is really a URL via DNS.\n\n    Args:\n        url: The URL to check for as QUrl, ideally via qurl_from_user_input.\n\n    Return:\n        True if the URL really is a URL, False otherwise.\n    \"\"\"\n    if not url.isValid():\n        return False\n    host = url.host()\n    log.url.debug(\"DNS request for {}\".format(host))\n    if not host:\n        return False\n    info = QHostInfo.fromName(host)\n    return not info.error()\n\n\ndef fuzzy_url(urlstr, cwd=None, relative=False, do_search=True):\n    \"\"\"Get a QUrl based on an user input which is URL or search term.\n\n    Args:\n        urlstr: URL to load as a string.\n        cwd: The current working directory, or None.\n        relative: Whether to resolve relative files.\n        do_search: Whether to perform a search on non-URLs.\n\n    Return:\n        A target QUrl to a searchpage or the original URL.\n    \"\"\"\n    expanded = os.path.expanduser(urlstr)\n    if relative and cwd:\n        path = os.path.join(cwd, expanded)\n    elif relative:\n        try:\n            path = os.path.abspath(expanded)\n        except OSError:\n            path = None\n    elif os.path.isabs(expanded):\n        path = expanded\n    else:\n        path = None\n\n    stripped = urlstr.strip()\n    if path is not None and os.path.exists(path):\n        log.url.debug(\"URL is a local file\")\n        url = QUrl.fromLocalFile(path)\n    elif (not do_search) or is_url(stripped):\n        # probably an address\n        log.url.debug(\"URL is a fuzzy address\")\n        url = qurl_from_user_input(urlstr)\n    else:  # probably a search term\n        log.url.debug(\"URL is a fuzzy search term\")\n        try:\n            url = _get_search_url(urlstr)\n        except ValueError:  # invalid search engine\n            url = qurl_from_user_input(stripped)\n    log.url.debug(\"Converting fuzzy term {} to URL -> {}\".format(\n                  urlstr, url.toDisplayString()))\n    if do_search and config.get('general', 'auto-search'):\n        qtutils.ensure_valid(url)\n    else:\n        if not url.isValid():\n            raise FuzzyUrlError(\"Invalid URL '{}'!\".format(urlstr))\n    return url\n\n\ndef _has_explicit_scheme(url):\n    \"\"\"Check if an url has an explicit scheme given.\n\n    Args:\n        url: The URL as QUrl.\n    \"\"\"\n    return url.isValid() and url.scheme() and not url.path().startswith(' ')\n\n\ndef is_special_url(url):\n    \"\"\"Return True if url is an about:... or other special URL.\n\n    Args:\n        url: The URL as QUrl.\n    \"\"\"\n    if not url.isValid():\n        return False\n    special_schemes = ('about', 'qute', 'file')\n    return url.scheme() in special_schemes\n\n\ndef is_url(urlstr):\n    \"\"\"Check if url seems to be a valid URL.\n\n    Args:\n        urlstr: The URL as string.\n\n    Return:\n        True if it is a valid URL, False otherwise.\n    \"\"\"\n    autosearch = config.get('general', 'auto-search')\n\n    log.url.debug(\"Checking if '{}' is a URL (autosearch={}).\".format(\n                  urlstr, autosearch))\n\n    urlstr = urlstr.strip()\n    qurl = QUrl(urlstr)\n\n    if not autosearch:\n        # no autosearch, so everything is a URL unless it has an explicit\n        # search engine.\n        engine, _term = _parse_search_term(urlstr)\n        if engine is None:\n            return True\n        else:\n            return False\n\n    if _has_explicit_scheme(qurl):\n        # URLs with explicit schemes are always URLs\n        log.url.debug(\"Contains explicit scheme\")\n        url = True\n    elif ' ' in urlstr:\n        # A URL will never contain a space\n        log.url.debug(\"Contains space -> no URL\")\n        url = False\n    elif is_special_url(qurl):\n        # Special URLs are always URLs, even with autosearch=False\n        log.url.debug(\"Is an special URL.\")\n        url = True\n    elif autosearch == 'dns':\n        log.url.debug(\"Checking via DNS\")\n        # We want to use qurl_from_user_input here, as the user might enter\n        # \"foo.de\" and that should be treated as URL here.\n        url = _is_url_dns(qurl_from_user_input(urlstr))\n    elif autosearch == 'naive':\n        log.url.debug(\"Checking via naive check\")\n        url = _is_url_naive(urlstr)\n    else:\n        raise ValueError(\"Invalid autosearch value\")\n    return url and qurl_from_user_input(urlstr).isValid()\n\n\ndef qurl_from_user_input(urlstr):\n    \"\"\"Get a QUrl based on an user input. Additionally handles IPv6 addresses.\n\n    QUrl.fromUserInput handles something like '::1' as a file URL instead of an\n    IPv6, so we first try to handle it as a valid IPv6, and if that fails we\n    use QUrl.fromUserInput.\n\n    WORKAROUND - https://bugreports.qt-project.org/browse/QTBUG-41089\n    FIXME - Maybe https://codereview.qt-project.org/#/c/93851/ has a better way\n            to solve this?\n    https://github.com/The-Compiler/qutebrowser/issues/109\n\n    Args:\n        urlstr: The URL as string.\n\n    Return:\n        The converted QUrl.\n    \"\"\"\n    # First we try very liberally to separate something like an IPv6 from the\n    # rest (e.g. path info or parameters)\n    match = re.match(r'\\[?([0-9a-fA-F:.]+)\\]?(.*)', urlstr.strip())\n    if match:\n        ipstr, rest = match.groups()\n    else:\n        ipstr = urlstr.strip()\n        rest = ''\n    # Then we try to parse it as an IPv6, and if we fail use\n    # QUrl.fromUserInput.\n    try:\n        ipaddress.IPv6Address(ipstr)\n    except ipaddress.AddressValueError:\n        return QUrl.fromUserInput(urlstr)\n    else:\n        return QUrl('http://[{}]{}'.format(ipstr, rest))\n\n\ndef invalid_url_error(win_id, url, action):\n    \"\"\"Display an error message for an URL.\n\n    Args:\n        win_id: The window ID to show the error message in.\n        action: The action which was interrupted by the error.\n    \"\"\"\n    if url.isValid():\n        raise ValueError(\"Calling invalid_url_error with valid URL {}\".format(\n            url.toDisplayString()))\n    errstring = \"Trying to {} with invalid URL\".format(action)\n    if url.errorString():\n        errstring += \" - {}\".format(url.errorString())\n    message.error(win_id, errstring)\n\n\ndef raise_cmdexc_if_invalid(url):\n    \"\"\"Check if the given QUrl is invalid, and if so, raise a CommandError.\"\"\"\n    if not url.isValid():\n        errstr = \"Invalid URL {}\".format(url.toDisplayString())\n        url_error = url.errorString()\n        if url_error:\n            errstr += \" - {}\".format(url_error)\n        raise cmdexc.CommandError(errstr)\n\n\ndef filename_from_url(url):\n    \"\"\"Get a suitable filename from an URL.\n\n    Args:\n        url: The URL to parse, as a QUrl.\n\n    Return:\n        The suggested filename as a string, or None.\n    \"\"\"\n    if not url.isValid():\n        return None\n    pathname = posixpath.basename(url.path())\n    if pathname:\n        return pathname\n    elif url.host():\n        return url.host() + '.html'\n    else:\n        return None\n\n\ndef host_tuple(url):\n    \"\"\"Get a (scheme, host, port) tuple.\n\n    This is suitable to identify a connection, e.g. for SSL errors.\n    \"\"\"\n    return (url.scheme(), url.host(), url.port())\n\n\nclass FuzzyUrlError(Exception):\n\n    \"\"\"Exception raised by fuzzy_url on problems.\"\"\"\n\n    pass\n", "path": "qutebrowser/utils/urlutils.py"}], "after_files": [{"content": "# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>\n#\n# This file is part of qutebrowser.\n#\n# qutebrowser is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# qutebrowser is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"Utils regarding URL handling.\"\"\"\n\nimport re\nimport os.path\nimport ipaddress\nimport posixpath\nimport urllib.parse\n\nfrom PyQt5.QtCore import QUrl\nfrom PyQt5.QtNetwork import QHostInfo, QHostAddress\n\nfrom qutebrowser.config import config, configexc\nfrom qutebrowser.utils import log, qtutils, message\nfrom qutebrowser.commands import cmdexc\n\n\n# FIXME: we probably could raise some exceptions on invalid URLs\n# https://github.com/The-Compiler/qutebrowser/issues/108\n\n\ndef _parse_search_term(s):\n    \"\"\"Get a search engine name and search term from a string.\n\n    Args:\n        s: The string to get a search engine for.\n\n    Return:\n        A (engine, term) tuple, where engine is None for the default engine.\n    \"\"\"\n    m = re.search(r'(^\\w+)\\s+(.+)($|\\s+)', s)\n    if m:\n        engine = m.group(1)\n        try:\n            config.get('searchengines', engine)\n        except configexc.NoOptionError:\n            engine = None\n            term = s\n        else:\n            term = m.group(2).rstrip()\n    else:\n        engine = None\n        term = s\n    log.url.debug(\"engine {}, term '{}'\".format(engine, term))\n    return (engine, term)\n\n\ndef _get_search_url(txt):\n    \"\"\"Get a search engine URL for a text.\n\n    Args:\n        txt: Text to search for.\n\n    Return:\n        The search URL as a QUrl.\n    \"\"\"\n    log.url.debug(\"Finding search engine for '{}'\".format(txt))\n    engine, term = _parse_search_term(txt)\n    if not term:\n        raise FuzzyUrlError(\"No search term given\")\n    if engine is None:\n        template = config.get('searchengines', 'DEFAULT')\n    else:\n        template = config.get('searchengines', engine)\n    url = qurl_from_user_input(template.format(urllib.parse.quote(term)))\n    qtutils.ensure_valid(url)\n    return url\n\n\ndef _is_url_naive(urlstr):\n    \"\"\"Naive check if given URL is really a URL.\n\n    Args:\n        urlstr: The URL to check for, as string.\n\n    Return:\n        True if the URL really is a URL, False otherwise.\n    \"\"\"\n    url = qurl_from_user_input(urlstr)\n    try:\n        ipaddress.ip_address(urlstr)\n    except ValueError:\n        pass\n    else:\n        # Valid IPv4/IPv6 address\n        return True\n\n    # Qt treats things like \"23.42\" or \"1337\" or \"0xDEAD\" as valid URLs\n    # which we don't want to. Note we already filtered *real* valid IPs\n    # above.\n    if not QHostAddress(urlstr).isNull():\n        return False\n\n    if not url.isValid():\n        return False\n    elif '.' in url.host():\n        return True\n    elif url.host() == 'localhost':\n        return True\n    else:\n        return False\n\n\ndef _is_url_dns(url):\n    \"\"\"Check if a URL is really a URL via DNS.\n\n    Args:\n        url: The URL to check for as QUrl, ideally via qurl_from_user_input.\n\n    Return:\n        True if the URL really is a URL, False otherwise.\n    \"\"\"\n    if not url.isValid():\n        return False\n    host = url.host()\n    log.url.debug(\"DNS request for {}\".format(host))\n    if not host:\n        return False\n    info = QHostInfo.fromName(host)\n    return not info.error()\n\n\ndef fuzzy_url(urlstr, cwd=None, relative=False, do_search=True):\n    \"\"\"Get a QUrl based on an user input which is URL or search term.\n\n    Args:\n        urlstr: URL to load as a string.\n        cwd: The current working directory, or None.\n        relative: Whether to resolve relative files.\n        do_search: Whether to perform a search on non-URLs.\n\n    Return:\n        A target QUrl to a searchpage or the original URL.\n    \"\"\"\n    expanded = os.path.expanduser(urlstr)\n    if relative and cwd:\n        path = os.path.join(cwd, expanded)\n    elif relative:\n        try:\n            path = os.path.abspath(expanded)\n        except OSError:\n            path = None\n    elif os.path.isabs(expanded):\n        path = expanded\n    else:\n        path = None\n\n    stripped = urlstr.strip()\n    if path is not None and os.path.exists(path):\n        log.url.debug(\"URL is a local file\")\n        url = QUrl.fromLocalFile(path)\n    elif (not do_search) or is_url(stripped):\n        # probably an address\n        log.url.debug(\"URL is a fuzzy address\")\n        url = qurl_from_user_input(urlstr)\n    else:  # probably a search term\n        log.url.debug(\"URL is a fuzzy search term\")\n        try:\n            url = _get_search_url(urlstr)\n        except ValueError:  # invalid search engine\n            url = qurl_from_user_input(stripped)\n    log.url.debug(\"Converting fuzzy term {} to URL -> {}\".format(\n                  urlstr, url.toDisplayString()))\n    if do_search and config.get('general', 'auto-search'):\n        qtutils.ensure_valid(url)\n    else:\n        if not url.isValid():\n            raise FuzzyUrlError(\"Invalid URL '{}'!\".format(urlstr))\n    return url\n\n\ndef _has_explicit_scheme(url):\n    \"\"\"Check if an url has an explicit scheme given.\n\n    Args:\n        url: The URL as QUrl.\n    \"\"\"\n\n    # Note that generic URI syntax actually would allow a second colon\n    # after the scheme delimiter. Since we don't know of any URIs\n    # using this and want to support e.g. searching for scoped C++\n    # symbols, we treat this as not an URI anyways.\n    return (url.isValid() and url.scheme()\n            and not url.path().startswith(' ')\n            and not url.path().startswith(':'))\n\n\ndef is_special_url(url):\n    \"\"\"Return True if url is an about:... or other special URL.\n\n    Args:\n        url: The URL as QUrl.\n    \"\"\"\n    if not url.isValid():\n        return False\n    special_schemes = ('about', 'qute', 'file')\n    return url.scheme() in special_schemes\n\n\ndef is_url(urlstr):\n    \"\"\"Check if url seems to be a valid URL.\n\n    Args:\n        urlstr: The URL as string.\n\n    Return:\n        True if it is a valid URL, False otherwise.\n    \"\"\"\n    autosearch = config.get('general', 'auto-search')\n\n    log.url.debug(\"Checking if '{}' is a URL (autosearch={}).\".format(\n                  urlstr, autosearch))\n\n    urlstr = urlstr.strip()\n    qurl = QUrl(urlstr)\n\n    if not autosearch:\n        # no autosearch, so everything is a URL unless it has an explicit\n        # search engine.\n        engine, _term = _parse_search_term(urlstr)\n        if engine is None:\n            return True\n        else:\n            return False\n\n    if _has_explicit_scheme(qurl):\n        # URLs with explicit schemes are always URLs\n        log.url.debug(\"Contains explicit scheme\")\n        url = True\n    elif ' ' in urlstr:\n        # A URL will never contain a space\n        log.url.debug(\"Contains space -> no URL\")\n        url = False\n    elif is_special_url(qurl):\n        # Special URLs are always URLs, even with autosearch=False\n        log.url.debug(\"Is an special URL.\")\n        url = True\n    elif autosearch == 'dns':\n        log.url.debug(\"Checking via DNS\")\n        # We want to use qurl_from_user_input here, as the user might enter\n        # \"foo.de\" and that should be treated as URL here.\n        url = _is_url_dns(qurl_from_user_input(urlstr))\n    elif autosearch == 'naive':\n        log.url.debug(\"Checking via naive check\")\n        url = _is_url_naive(urlstr)\n    else:\n        raise ValueError(\"Invalid autosearch value\")\n    return url and qurl_from_user_input(urlstr).isValid()\n\n\ndef qurl_from_user_input(urlstr):\n    \"\"\"Get a QUrl based on an user input. Additionally handles IPv6 addresses.\n\n    QUrl.fromUserInput handles something like '::1' as a file URL instead of an\n    IPv6, so we first try to handle it as a valid IPv6, and if that fails we\n    use QUrl.fromUserInput.\n\n    WORKAROUND - https://bugreports.qt-project.org/browse/QTBUG-41089\n    FIXME - Maybe https://codereview.qt-project.org/#/c/93851/ has a better way\n            to solve this?\n    https://github.com/The-Compiler/qutebrowser/issues/109\n\n    Args:\n        urlstr: The URL as string.\n\n    Return:\n        The converted QUrl.\n    \"\"\"\n    # First we try very liberally to separate something like an IPv6 from the\n    # rest (e.g. path info or parameters)\n    match = re.match(r'\\[?([0-9a-fA-F:.]+)\\]?(.*)', urlstr.strip())\n    if match:\n        ipstr, rest = match.groups()\n    else:\n        ipstr = urlstr.strip()\n        rest = ''\n    # Then we try to parse it as an IPv6, and if we fail use\n    # QUrl.fromUserInput.\n    try:\n        ipaddress.IPv6Address(ipstr)\n    except ipaddress.AddressValueError:\n        return QUrl.fromUserInput(urlstr)\n    else:\n        return QUrl('http://[{}]{}'.format(ipstr, rest))\n\n\ndef invalid_url_error(win_id, url, action):\n    \"\"\"Display an error message for an URL.\n\n    Args:\n        win_id: The window ID to show the error message in.\n        action: The action which was interrupted by the error.\n    \"\"\"\n    if url.isValid():\n        raise ValueError(\"Calling invalid_url_error with valid URL {}\".format(\n            url.toDisplayString()))\n    errstring = \"Trying to {} with invalid URL\".format(action)\n    if url.errorString():\n        errstring += \" - {}\".format(url.errorString())\n    message.error(win_id, errstring)\n\n\ndef raise_cmdexc_if_invalid(url):\n    \"\"\"Check if the given QUrl is invalid, and if so, raise a CommandError.\"\"\"\n    if not url.isValid():\n        errstr = \"Invalid URL {}\".format(url.toDisplayString())\n        url_error = url.errorString()\n        if url_error:\n            errstr += \" - {}\".format(url_error)\n        raise cmdexc.CommandError(errstr)\n\n\ndef filename_from_url(url):\n    \"\"\"Get a suitable filename from an URL.\n\n    Args:\n        url: The URL to parse, as a QUrl.\n\n    Return:\n        The suggested filename as a string, or None.\n    \"\"\"\n    if not url.isValid():\n        return None\n    pathname = posixpath.basename(url.path())\n    if pathname:\n        return pathname\n    elif url.host():\n        return url.host() + '.html'\n    else:\n        return None\n\n\ndef host_tuple(url):\n    \"\"\"Get a (scheme, host, port) tuple.\n\n    This is suitable to identify a connection, e.g. for SSL errors.\n    \"\"\"\n    return (url.scheme(), url.host(), url.port())\n\n\nclass FuzzyUrlError(Exception):\n\n    \"\"\"Exception raised by fuzzy_url on problems.\"\"\"\n\n    pass\n", "path": "qutebrowser/utils/urlutils.py"}]}