Make sure early terminations of _fetch_over_network happen via an exception, not by returning None. Factor out certificate handling interface.
This commit is contained in:
parent
4e8f3dcd05
commit
f45630450f
96
av98.py
96
av98.py
|
@ -190,6 +190,9 @@ CRLF = '\r\n'
|
||||||
def looks_like_url(word):
|
def looks_like_url(word):
|
||||||
return "." in word and word.startswith("gemini://")
|
return "." in word and word.startswith("gemini://")
|
||||||
|
|
||||||
|
class UserAbortException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
# GeminiClient Decorators
|
# GeminiClient Decorators
|
||||||
def needs_gi(inner):
|
def needs_gi(inner):
|
||||||
def outer(self, *args, **kwargs):
|
def outer(self, *args, **kwargs):
|
||||||
|
@ -335,6 +338,8 @@ you'll be able to transparently follow links to Gopherspace!""")
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
gi, mime, body, tmpfile = self._fetch_over_network(gi)
|
gi, mime, body, tmpfile = self._fetch_over_network(gi)
|
||||||
|
except UserAbortException:
|
||||||
|
return
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Print an error message
|
# Print an error message
|
||||||
if isinstance(err, socket.gaierror):
|
if isinstance(err, socket.gaierror):
|
||||||
|
@ -385,7 +390,7 @@ you'll be able to transparently follow links to Gopherspace!""")
|
||||||
self._deactivate_client_cert()
|
self._deactivate_client_cert()
|
||||||
else:
|
else:
|
||||||
print("Staying here.")
|
print("Staying here.")
|
||||||
|
raise UserAbortException()
|
||||||
else:
|
else:
|
||||||
print("PRIVACY ALERT: Deactivate client cert before connecting to a new domain?")
|
print("PRIVACY ALERT: Deactivate client cert before connecting to a new domain?")
|
||||||
resp = input("Y/N? ")
|
resp = input("Y/N? ")
|
||||||
|
@ -462,7 +467,7 @@ you'll be able to transparently follow links to Gopherspace!""")
|
||||||
else:
|
else:
|
||||||
follow = "yes"
|
follow = "yes"
|
||||||
if follow.strip().lower() not in ("y", "yes"):
|
if follow.strip().lower() not in ("y", "yes"):
|
||||||
return
|
raise UserAbortException()
|
||||||
self._debug("Following redirect to %s." % new_gi.url)
|
self._debug("Following redirect to %s." % new_gi.url)
|
||||||
self._debug("This is consecutive redirect number %d." % len(self.previous_redirectors))
|
self._debug("This is consecutive redirect number %d." % len(self.previous_redirectors))
|
||||||
self.previous_redirectors.add(gi.url)
|
self.previous_redirectors.add(gi.url)
|
||||||
|
@ -477,49 +482,8 @@ you'll be able to transparently follow links to Gopherspace!""")
|
||||||
|
|
||||||
# Client cert
|
# Client cert
|
||||||
elif status.startswith("6"):
|
elif status.startswith("6"):
|
||||||
# Don't do client cert stuff in restricted mode, as in principle
|
self._handle_cert_request(meta)
|
||||||
# it could be used to fill up the disk by creating a whole lot of
|
return self._fetch_over_network(gi)
|
||||||
# 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
|
|
||||||
|
|
||||||
# Invalid status
|
# Invalid status
|
||||||
elif not status.startswith("2"):
|
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():
|
for _, filename in self.cache.values():
|
||||||
assert os.path.isfile(filename)
|
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):
|
def _validate_cert(self, address, host, cert):
|
||||||
"""
|
"""
|
||||||
Validate a TLS certificate in TOFU mode.
|
Validate a TLS certificate in TOFU mode.
|
||||||
|
|
Loading…
Reference in New Issue