diff --git a/letsencrypt/client/challenge.py b/letsencrypt/client/challenge.py index 6eaee82f7..719b04945 100644 --- a/letsencrypt/client/challenge.py +++ b/letsencrypt/client/challenge.py @@ -6,17 +6,25 @@ from letsencrypt.client import logger class Challenge(object): + """Let's Encrypt challenge.""" def __init__(self, configurator): self.config = configurator def perform(self, quiet=True): + """Perform the challange. + + :param bool quiet: TODO + + """ raise NotImplementedError() def generate_response(self): + """Generate response.""" raise NotImplementedError() def cleanup(self): + """Cleanup.""" raise NotImplementedError() @@ -46,10 +54,10 @@ def gen_challenge_path(challenges, combos=None): def _find_smart_path(challenges, combos): - """ - Can be called if combinations is included - Function uses a simple ranking system to choose the combo with the - lowest cost + """Find challenge path with server hints. + + Can be called if combinations is included. Function uses a simple + ranking system to choose the combo with the lowest cost. :param list challenges: A list of challenges from ACME "challenge" server message to be fulfilled by the client in order to prove @@ -93,10 +101,11 @@ def _find_smart_path(challenges, combos): def _find_dumb_path(challenges): - """ - Should be called if the combinations hint is not included by the server - This function returns the best path that does not contain multiple - mutually exclusive challenges + """Find challange path without server hints. + + Should be called if the combinations hint is not included by the + server. This function returns the best path that does not contain + multiple mutually exclusive challenges. :param list challanges: A list of challenges from ACME "challenge" server message to be fulfilled by the client in order to prove diff --git a/letsencrypt/client/client.py b/letsencrypt/client/client.py index 490daebf4..006f6289e 100644 --- a/letsencrypt/client/client.py +++ b/letsencrypt/client/client.py @@ -31,61 +31,58 @@ ALLOW_RAW_IPV6_SERVER = False class Client(object): - """ACME protocol client.""" + """ACME protocol client. + + :ivar config: Configurator. + :type config: :class:`letsencrypt.client.configurator.Configurator` + + :ivar str server: Certificate authority server + :ivar str server_url: Full URL of the CSR server + + :ivar csr: Certificate Signing Request + :type csr: :class:`CSR` + + :ivar list names: Domain names (:class:`list` of :class:`str`). + + :ivar privkey: Private key + :type privkey: :class:`Key` + + :ivar bool use_curses: Use curses UI + + """ Key = collections.namedtuple("Key", "file pem") CSR = collections.namedtuple("CSR", "file data type") - def __init__(self, ca_server, cert_signing_request=CSR(None, None, None), - private_key=Key(None, None), use_curses=True): - """Initialize client. + def __init__(self, server, csr=CSR(None, None, None), + privkey=Key(None, None), use_curses=True): + """Initialize a client.""" + self.server = server + self.server_url = "https://%s/acme/" % self.server + self.names = [] + self.use_curses = use_curses - :param str ca_server: Certificate authority server - - :param cert_signing_request: Certificate Signing Request - :type cert_signing_request: :class:`CSR` - - :param private_key: Private key - :type private_key: :class:`Key` - - :param bool use_curses: Use curses UI - - """ - self.curses = use_curses + self.csr = csr + self.privkey = privkey + self._validate_csr_key_cli() # TODO: catch exceptions # Logger needs to be initialized before Configurator self.init_logger() + # TODO: Can probably figure out which configurator to use # without special packaging based on system info Command # line arg or client function to discover self.config = apache_configurator.ApacheConfigurator( CONFIG.SERVER_ROOT) - self.server = ca_server - # These are CSR/Key namedtuples - self.csr = cert_signing_request - self.privkey = private_key - - # TODO: Figure out all exceptions from this function - try: - self._validate_csr_key_cli() - - except errors.LetsEncryptClientError as exc: - # TODO: Something nice here... - logger.fatal(("%s - until the programmers get their act together, " - "we are just going to exit" % str(exc))) - sys.exit(1) - self.server_url = "https://%s/acme/" % self.server - - def authenticate(self, domains=None, redirect=None, eula=False): + def authenticate(self, domains=None, eula=False, redirect=None): """ :param list domains: List of domains - - :param redirect: - :type redirect: bool or None - :param bool eula: EULA accepted + :param redirect: If traffic should be forwarded from HTTP to HTTPS. + :type redirect: bool or None + :raises errors.LetsEncryptClientError: CSR does not contain one of the specified names. @@ -425,27 +422,29 @@ class Client(object): self.config.enable_site(host) # sites may have been enabled / final cleanup - self.config.restart(quiet=self.curses) + self.config.restart(quiet=self.use_curses) display.success_installation(self.names) return cert_file - def optimize_config(self, vhost, redirect): + def optimize_config(self, vhost, redirect=None): """Optimize the configuration. :param vhost: vhost to optimize :type vhost: :class:`apache_configurator.VH` - :param bool redirect: If traffic should be forwarded from HTTP to HTTPS + :param redirect: If traffic should be forwarded from HTTP to HTTPS. + :type redirect: bool or None """ + # TODO: this should most definitely be moved to __init__ if redirect is None: redirect = display.redirect_by_default() if redirect: self.redirect_to_ssl(vhost) - self.config.restart(quiet=self.curses) + self.config.restart(quiet=self.use_curses) # if self.ocsp_stapling is None: # q = ("Would you like to protect the privacy of your users " @@ -733,7 +732,7 @@ class Client(object): return names def init_logger(self): - if self.curses: + if self.use_curses: logger.setLogger(logger.NcursesLogger()) logger.setLogLevel(logger.INFO) else: diff --git a/letsencrypt/client/interactive_challenge.py b/letsencrypt/client/interactive_challenge.py index 720a0381d..4f93f1e4f 100644 --- a/letsencrypt/client/interactive_challenge.py +++ b/letsencrypt/client/interactive_challenge.py @@ -5,14 +5,15 @@ import dialog from letsencrypt.client import challenge -########################################################### -# Interactive challenge displays the string sent by the CA -# formatted to fit on the screen of the client -# The Challenge also adds proper instructions for how the -# client should continue the letsencrypt process -########################################################### +class InteractiveChallenge(challenge.Challenge): + """Interactive challange. -class Interactive_Challenge(challenge.Challenge): + Interactive challenge displays the string sent by the CA formatted + to fit on the screen of the client. The Challenge also adds proper + instructions for how the client should continue the letsencrypt + process. + + """ BOX_SIZE = 70 def __init__(self, string): @@ -20,16 +21,17 @@ class Interactive_Challenge(challenge.Challenge): def perform(self, quiet=True): if quiet: - dialog.Dialog().msgbox(self.get_display_string(), width=self.BOX_SIZE) + dialog.Dialog().msgbox( + self.get_display_string(), width=self.BOX_SIZE) else: print self.get_display_string() raw_input('') return True - def get_display_string(self): - return textwrap.fill(self.string, width=self.BOX_SIZE) + "\n\nPlease Press Enter to Continue" + return (textwrap.fill(self.string, width=self.BOX_SIZE) + + "\n\nPlease Press Enter to Continue") # def formatted_reasons(self): # return "\n\t* %s\n", self.reason diff --git a/letsencrypt/scripts/main.py b/letsencrypt/scripts/main.py index 98b5587b6..fc63e5b62 100755 --- a/letsencrypt/scripts/main.py +++ b/letsencrypt/scripts/main.py @@ -97,7 +97,7 @@ def main(): if args.revoke: acme.list_certs_keys() else: - acme.authenticate(args.domains, args.redirect, args.eula) + acme.authenticate(args.domains, args.eula, args.redirect) def read_file(filename):