~sotirisp/qute-gemini

677acae9e2230088324d4bbce3f2b4246a6f5e40 — Sotiris Papatheodorou 8 months ago cf969b9
Show error page for failed requests
1 files changed, 87 insertions(+), 21 deletions(-)

M qute-gemini
M qute-gemini => qute-gemini +87 -21
@@ 19,6 19,43 @@ import urllib.parse
from typing import Tuple


_error_page_template = '''<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Error opening page: URL</title>
<style>
CSS
</style>
</head>
<body>
<h1>qute-gemini error</h1>
<p>Error while opening:<br/><a href="URL">URL_TEXT</a></p>
<p>DESCRIPTION</p>
</body>
</html>
'''


_status_code_desc = {
    "1":  "Gemini status code 1 Input. This is not implemented in qute-gemini.",
    "10": "Gemini status code 10 Input. This is not implemented in qute-gemini.",
    "11": "Gemini status code 11 Sensitive Input. This is not implemented in qute-gemini.",
    "4":  "Gemini status code 4 Temporary Failure. Server message: META",
    "40": "Gemini status code 40 Temporary Failure. Server message: META",
    "41": "Gemini status code 41 Server Unavailable. The server is unavailable due to overload or maintenance. Server message: META",
    "42": "Gemini status code 42 CGI Error. A CGI process, or similar system for generating dynamic content, died unexpectedly or timed out. Server message: META",
    "43": "Gemini status code 43 Proxy Error. A proxy request failed because the server was unable to successfully complete a transaction with the remote host. Server message: META",
    "44": "Gemini status code 44 Slow Down. Rate limiting is in effect. Please wait META seconds before making another request to this server.",
    "5":  "Gemini status code 5 Permanent Failure. Server message: META",
    "50": "Gemini status code 50 Permanent Failure. Server message: META",
    "51": "Gemini status code 51 Not Found. he requested resource could not be found but may be available in the future. Server message: META",
    "52": "Gemini status code 52 Gone. The resource requested is no longer available and will not be available again. Server message: META",
    "53": "Gemini status code 53 Proxy Request Refused. The request was for a resource at a domain not served by the server and the server does not accept proxy requests. Server message: META",
    "59": "Gemini status code 59 Bad Request. The server was unable to parse the client's request, presumably due to a malformed request. Server message: META",
    "6":  "Gemini status code 6 Client Certificate Required. This is not implemented in qute-gemini.",
}


def qute_url() -> str:
    """Get the URL passed to the script by qutebrowser."""
    return os.environ["QUTE_URL"]


@@ 82,27 119,40 @@ def gemini_fetch_url(url: str) -> Tuple[str, str]:
        # Get header and check for redirects
        fp = s.makefile("rb")
        header = fp.readline().decode("UTF-8").strip()
        status, media_type = header.split()[:2]
        status, meta = header.split()[:2]
        # Follow redirects
        if status.startswith("3"):
            url = gemini_absolutise_url(url, media_type)
            url = gemini_absolutise_url(url, meta)
            parsed_url = urllib.parse.urlparse(url)
        # Otherwise we're done
        else:
            break
    # Fail if transaction was not successful, user input or a certificate was
    # required
    # Process the response
    content = ""
    error_msg = ""
    # 2x Success
    if status.startswith("2"):
        if media_type.startswith("text/gemini"):
        if meta.startswith("text/gemini"):
            # Decode according to declared charset
            media_type, media_type_opts = cgi.parse_header(media_type)
            body = fp.read()
            body = body.decode(media_type_opts.get("charset", "UTF-8"))
            return body, ""
            media_type, media_type_opts = cgi.parse_header(meta)
            content = fp.read()
            content = content.decode(media_type_opts.get("charset", "UTF-8"))
        else:
            return "", "Received non-gemtext content: " + media_type
            error_msg = "Expected media type text/gemini but received " \
                + media_type
    # Handle errors
    else:
        return "", "Received Gemini status " + status
        # Try matching a 2-digit status code before trying to match a 1-digit
        # one
        try:
            error_msg = _status_code_desc[status[0:2]]
        except KeyError:
            try:
                error_msg = _status_code_desc[status[0]]
            except KeyError:
                error_msg = "The server sent back something weird."
        error_msg = error_msg.replace("META", meta)
    return content, error_msg


def gemtext_to_html(gemtext: str, title: str, url: str) -> str:


@@ 193,24 243,40 @@ def get_css() -> str:
        return ""


def qute_error_page(url: str, description: str) -> str:
    """Return a data URI error page like qutebrowser does.

    url:         The URL of the page that failed to load.
    description: A description of the error.
    Returns a data URI containing the error page.
    """
    # Generate the HTML error page
    html_page = _error_page_template.replace("URL", url)
    html_page = html_page.replace("URL_TEXT", html.escape(url))
    html_page = html_page.replace("DESCRIPTION", html.escape(description))
    html_page = html_page.replace("CSS", get_css())
    # URL encode and return as a data URI
    return "data:text/html;charset=UTF-8," + urllib.parse.quote(html_page)


def open_gemini(url: str, open_args: str) -> None:
    """Open Gemini URL in qutebrowser."""
    # Get the Gemini content
    content, error_msg = gemini_fetch_url(url)
    if error_msg:
        print(error_msg)
        sys.exit(1)

    # Convert to HTML in a temporary file
    tmpf = tempfile.NamedTemporaryFile("w", suffix=".html", delete=False)
    tmp_filename = tmpf.name
    tmpf.close()
    with open(tmp_filename, "w") as f:
        f.write(gemtext_to_html(content, url, url))
        open_url = qute_error_page(url, error_msg)
    else:
        # Convert to HTML in a temporary file
        tmpf = tempfile.NamedTemporaryFile("w", suffix=".html", delete=False)
        tmp_filename = tmpf.name
        tmpf.close()
        with open(tmp_filename, "w") as f:
            f.write(gemtext_to_html(content, url, url))
        open_url = " file://" + tmp_filename

    # Open the HTML file in qutebrowser
    with open(qute_fifo(), "w") as qfifo:
        qfifo.write("open " + open_args + " file://" + tmp_filename)
        qfifo.write("open " + open_args + open_url)


def open_other(url: str, open_args: str) -> None: