"""Gemini-related features of the browser.""" from bebop.browser.browser import Browser from bebop.navigation import set_parameter from bebop.page import Page from bebop.protocol import Request, Response def open_gemini_url(browser: Browser, url, redirects=0, history=True, use_cache=True): """Open a Gemini URL and set the formatted response as content. After initiating the connection, TODO """ browser.set_status(f"Loading {url}") if use_cache and url in browser.cache: browser.load_page(browser.cache[url]) if browser.current_url and history: browser.history.push(browser.current_url) browser.current_url = url browser.set_status(url) return req = Request(url, browser.stash) connected = req.connect() if not connected: if req.state == Request.STATE_ERROR_CERT: error = f"Certificate was missing or corrupt ({url})." elif req.state == Request.STATE_UNTRUSTED_CERT: error = f"Certificate has been changed ({url})." # TODO propose the user ways to handle this. elif req.state == Request.STATE_CONNECTION_FAILED: error_details = f": {req.error}" if req.error else "." error = f"Connection failed ({url})" + error_details else: error = f"Connection failed ({url})." browser.set_status_error(error) return if req.state == Request.STATE_INVALID_CERT: # TODO propose abort / temp trust pass elif req.state == Request.STATE_UNKNOWN_CERT: # TODO propose abort / temp trust / perm trust pass else: pass # TODO data = req.proceed() if not data: browser.set_status_error(f"Server did not respond in time ({url}).") return response = Response.parse(data) if not response: browser.set_status_error(f"Server response parsing failed ({url}).") return if response.code == 20: handle_code = handle_response_content(browser, response) if handle_code == 0: if browser.current_url and history: browser.history.push(browser.current_url) browser.current_url = url browser.cache[url] = browser.page_pad.current_page browser.set_status(url) elif handle_code == 1: browser.set_status(f"Downloaded {url}.") elif response.generic_code == 30 and response.meta: browser.open_url(response.meta, base_url=url, redirects=redirects + 1) elif response.generic_code in (40, 50): error = f"Server error: {response.meta or Response.code.name}" browser.set_status_error(error) elif response.generic_code == 10: handle_input_request(browser, url, response.meta) else: error = f"Unhandled response code {response.code}" browser.set_status_error(error) def handle_response_content(browser: Browser, response: Response) -> int: """Handle a response's content from a Gemini server. According to the MIME type received or inferred, render or download the response's content. Currently only text/gemini content is rendered. Arguments: - response: a successful Response. Returns: An error code: 0 means a page has been loaded, so any book-keeping such as history management can be applied; 1 means a content has been successfully retrieved but has not been displayed (e.g. non-text content) nor saved as a page; 2 means that the content could not be handled, either due to bogus MIME type or MIME parameters. """ mime_type = response.get_mime_type() if mime_type.main_type == "text": if mime_type.sub_type == "gemini": encoding = mime_type.charset try: text = response.content.decode(encoding, errors="replace") except LookupError: browser.set_status_error("Unknown encoding {encoding}.") return 2 browser.load_page(Page.from_gemtext(text)) return 0 else: pass # TODO else: pass # TODO return 1 def handle_input_request(browser: Browser, from_url: str, message: str =None): """Focus command-line to pass input to the server.""" if message: browser.set_status(f"Input needed: {message}") else: browser.set_status("Input needed:") user_input = browser.command_line.focus("?") if user_input: url = set_parameter(from_url, user_input) open_gemini_url(browser, url)