commit f45630450f6e7780de4b03c039beae98877e6bb6
parent 4e8f3dcd058d2c82f6aae43c8161ec3126d552e8
Author: Solderpunk <solderpunk@sdf.org>
Date: Mon, 31 Aug 2020 21:18:15 +0200
Make sure early terminations of _fetch_over_network happen via an exception, not by returning None. Factor out certificate handling interface.
Diffstat:
M | av98.py | | | 96 | ++++++++++++++++++++++++++++++++++++++++++------------------------------------- |
1 file changed, 51 insertions(+), 45 deletions(-)
diff --git a/av98.py b/av98.py
@@ -190,6 +190,9 @@ CRLF = '\r\n'
def looks_like_url(word):
return "." in word and word.startswith("gemini://")
+class UserAbortException(Exception):
+ pass
+
# GeminiClient Decorators
def needs_gi(inner):
def outer(self, *args, **kwargs):
@@ -335,6 +338,8 @@ you'll be able to transparently follow links to Gopherspace!""")
else:
try:
gi, mime, body, tmpfile = self._fetch_over_network(gi)
+ except UserAbortException:
+ return
except Exception as err:
# Print an error message
if isinstance(err, socket.gaierror):
@@ -385,7 +390,7 @@ you'll be able to transparently follow links to Gopherspace!""")
self._deactivate_client_cert()
else:
print("Staying here.")
-
+ raise UserAbortException()
else:
print("PRIVACY ALERT: Deactivate client cert before connecting to a new domain?")
resp = input("Y/N? ")
@@ -462,7 +467,7 @@ you'll be able to transparently follow links to Gopherspace!""")
else:
follow = "yes"
if follow.strip().lower() not in ("y", "yes"):
- return
+ raise UserAbortException()
self._debug("Following redirect to %s." % new_gi.url)
self._debug("This is consecutive redirect number %d." % len(self.previous_redirectors))
self.previous_redirectors.add(gi.url)
@@ -477,49 +482,8 @@ you'll be able to transparently follow links to Gopherspace!""")
# Client cert
elif status.startswith("6"):
- # Don't do client cert stuff in restricted mode, as in principle
- # it could be used to fill up the disk by creating a whole lot of
- # certificates
- if self.restricted:
- print("The server is requesting a client certificate.")
- print("These are not supported in restricted mode, sorry.")
- return
-
- print("SERVER SAYS: ", meta)
- # Present different messages for different 6x statuses, but
- # handle them the same.
- if status in ("64", "65"):
- print("The server rejected your certificate because it is either expired or not yet valid.")
- elif status == "63":
- print("The server did not accept your certificate.")
- print("You may need to e.g. coordinate with the admin to get your certificate fingerprint whitelisted.")
- else:
- print("The site {} is requesting a client certificate.".format(gi.host))
- print("This will allow the site to recognise you across requests.")
-
- # Give the user choices
- print("What do you want to do?")
- print("1. Give up.")
- print("2. Generate a new transient certificate.")
- print("3. Generate a new persistent certificate.")
- print("4. Load a previously generated persistent.")
- print("5. Load certificate from an external file.")
- choice = input("> ").strip()
- if choice == "2":
- self._generate_transient_cert_cert()
- self._go_to_gi(gi, update_hist, handle)
- elif choice == "3":
- self._generate_persistent_client_cert()
- self._go_to_gi(gi, update_hist, handle)
- elif choice == "4":
- self._choose_client_cert()
- self._go_to_gi(gi, update_hist, handle)
- elif choice == "5":
- self._load_client_cert()
- self._go_to_gi(gi, update_hist, handle)
- else:
- print("Giving up.")
- return
+ self._handle_cert_request(meta)
+ return self._fetch_over_network(gi)
# Invalid status
elif not status.startswith("2"):
@@ -732,6 +696,48 @@ you'll be able to transparently follow links to Gopherspace!""")
for _, filename in self.cache.values():
assert os.path.isfile(filename)
+ def _handle_cert_request(self, meta):
+
+ # Don't do client cert stuff in restricted mode, as in principle
+ # it could be used to fill up the disk by creating a whole lot of
+ # certificates
+ if self.restricted:
+ print("The server is requesting a client certificate.")
+ print("These are not supported in restricted mode, sorry.")
+ raise UserAbortException()
+
+ print("SERVER SAYS: ", meta)
+ # Present different messages for different 6x statuses, but
+ # handle them the same.
+ if status in ("64", "65"):
+ print("The server rejected your certificate because it is either expired or not yet valid.")
+ elif status == "63":
+ print("The server did not accept your certificate.")
+ print("You may need to e.g. coordinate with the admin to get your certificate fingerprint whitelisted.")
+ else:
+ print("The site {} is requesting a client certificate.".format(gi.host))
+ print("This will allow the site to recognise you across requests.")
+
+ # Give the user choices
+ print("What do you want to do?")
+ print("1. Give up.")
+ print("2. Generate a new transient certificate.")
+ print("3. Generate a new persistent certificate.")
+ print("4. Load a previously generated persistent.")
+ print("5. Load certificate from an external file.")
+ choice = input("> ").strip()
+ if choice == "2":
+ self._generate_transient_cert_cert()
+ elif choice == "3":
+ self._generate_persistent_client_cert()
+ elif choice == "4":
+ self._choose_client_cert()
+ elif choice == "5":
+ self._load_client_cert()
+ else:
+ print("Giving up.")
+ raise UserAbortException()
+
def _validate_cert(self, address, host, cert):
"""
Validate a TLS certificate in TOFU mode.