We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Domain with german umlauts ('ascii' codec can't encode character)
CentOS 7.1.1503

I tried to create an certificat with domain contains german umlauts. Domain is: entä.de
I getting in the console this error:

```
An unexpected error occurred:
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 3: ordinal not in range(128)
Please see the logfiles in /var/log/letsencrypt for more details.
```

Logfile:

``` python
2015-12-08 17:58:44,353:DEBUG:letsencrypt.cli:Exiting abnormally:
Traceback (most recent call last):
  File "/root/.local/share/letsencrypt/bin/letsencrypt", line 11, in <module>
    sys.exit(main())
  File "/root/.local/share/letsencrypt/lib/python2.7/site-packages/letsencrypt/cli.py", line 1283, in main
    return args.func(args, config, plugins)
  File "/root/.local/share/letsencrypt/lib/python2.7/site-packages/letsencrypt/cli.py", line 511, in obtain_cert
    domains = _find_domains(args, installer)
  File "/root/.local/share/letsencrypt/lib/python2.7/site-packages/letsencrypt/cli.py", line 106, in _find_domains
    domains = display_ops.choose_names(installer)
  File "/root/.local/share/letsencrypt/lib/python2.7/site-packages/letsencrypt/display/ops.py", line 187, in choose_names
    return _choose_names_manually()
  File "/root/.local/share/letsencrypt/lib/python2.7/site-packages/letsencrypt/display/ops.py", line 235, in _choose_names_manually
    return display_util.separate_list_input(input_)
  File "/root/.local/share/letsencrypt/lib/python2.7/site-packages/letsencrypt/display/util.py", line 419, in separate_list_input
    return [str(string) for string in no_commas.split()]
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 3: ordinal not in range(128)
```

Is there a way to fix this?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `letsencrypt/display/ops.py`
Content:
```
1 """Contains UI methods for LE user operations."""
2 import logging
3 import os
4 
5 import zope.component
6 
7 from letsencrypt import errors
8 from letsencrypt import interfaces
9 from letsencrypt import le_util
10 from letsencrypt.display import util as display_util
11 
12 
13 logger = logging.getLogger(__name__)
14 
15 # Define a helper function to avoid verbose code
16 util = zope.component.getUtility
17 
18 
19 def choose_plugin(prepared, question):
20     """Allow the user to choose their plugin.
21 
22     :param list prepared: List of `~.PluginEntryPoint`.
23     :param str question: Question to be presented to the user.
24 
25     :returns: Plugin entry point chosen by the user.
26     :rtype: `~.PluginEntryPoint`
27 
28     """
29     opts = [plugin_ep.description_with_name +
30             (" [Misconfigured]" if plugin_ep.misconfigured else "")
31             for plugin_ep in prepared]
32 
33     while True:
34         code, index = util(interfaces.IDisplay).menu(
35             question, opts, help_label="More Info")
36 
37         if code == display_util.OK:
38             plugin_ep = prepared[index]
39             if plugin_ep.misconfigured:
40                 util(interfaces.IDisplay).notification(
41                     "The selected plugin encountered an error while parsing "
42                     "your server configuration and cannot be used. The error "
43                     "was:\n\n{0}".format(plugin_ep.prepare()),
44                     height=display_util.HEIGHT, pause=False)
45             else:
46                 return plugin_ep
47         elif code == display_util.HELP:
48             if prepared[index].misconfigured:
49                 msg = "Reported Error: %s" % prepared[index].prepare()
50             else:
51                 msg = prepared[index].init().more_info()
52             util(interfaces.IDisplay).notification(
53                 msg, height=display_util.HEIGHT)
54         else:
55             return None
56 
57 
58 def pick_plugin(config, default, plugins, question, ifaces):
59     """Pick plugin.
60 
61     :param letsencrypt.interfaces.IConfig: Configuration
62     :param str default: Plugin name supplied by user or ``None``.
63     :param letsencrypt.plugins.disco.PluginsRegistry plugins:
64         All plugins registered as entry points.
65     :param str question: Question to be presented to the user in case
66         multiple candidates are found.
67     :param list ifaces: Interfaces that plugins must provide.
68 
69     :returns: Initialized plugin.
70     :rtype: IPlugin
71 
72     """
73     if default is not None:
74         # throw more UX-friendly error if default not in plugins
75         filtered = plugins.filter(lambda p_ep: p_ep.name == default)
76     else:
77         filtered = plugins.visible().ifaces(ifaces)
78 
79     filtered.init(config)
80     verified = filtered.verify(ifaces)
81     verified.prepare()
82     prepared = verified.available()
83 
84     if len(prepared) > 1:
85         logger.debug("Multiple candidate plugins: %s", prepared)
86         plugin_ep = choose_plugin(prepared.values(), question)
87         if plugin_ep is None:
88             return None
89         else:
90             return plugin_ep.init()
91     elif len(prepared) == 1:
92         plugin_ep = prepared.values()[0]
93         logger.debug("Single candidate plugin: %s", plugin_ep)
94         if plugin_ep.misconfigured:
95             return None
96         return plugin_ep.init()
97     else:
98         logger.debug("No candidate plugin")
99         return None
100 
101 
102 def pick_authenticator(
103         config, default, plugins, question="How would you "
104         "like to authenticate with the Let's Encrypt CA?"):
105     """Pick authentication plugin."""
106     return pick_plugin(
107         config, default, plugins, question, (interfaces.IAuthenticator,))
108 
109 
110 def pick_installer(config, default, plugins,
111                    question="How would you like to install certificates?"):
112     """Pick installer plugin."""
113     return pick_plugin(
114         config, default, plugins, question, (interfaces.IInstaller,))
115 
116 
117 def pick_configurator(
118         config, default, plugins,
119         question="How would you like to authenticate and install "
120                  "certificates?"):
121     """Pick configurator plugin."""
122     return pick_plugin(
123         config, default, plugins, question,
124         (interfaces.IAuthenticator, interfaces.IInstaller))
125 
126 def get_email(more=False, invalid=False):
127     """Prompt for valid email address.
128 
129     :param bool more: explain why the email is strongly advisable, but how to
130         skip it
131     :param bool invalid: true if the user just typed something, but it wasn't
132         a valid-looking email
133 
134     :returns: Email or ``None`` if cancelled by user.
135     :rtype: str
136 
137     """
138     msg = "Enter email address (used for urgent notices and lost key recovery)"
139     if invalid:
140         msg = "There seem to be problems with that address. " + msg
141     if more:
142         msg += ('\n\nIf you really want to skip this, you can run the client with '
143                 '--register-unsafely-without-email but make sure you backup your '
144                 'account key from /etc/letsencrypt/accounts\n\n')
145     code, email = zope.component.getUtility(interfaces.IDisplay).input(msg)
146 
147     if code == display_util.OK:
148         if le_util.safe_email(email):
149             return email
150         else:
151             # TODO catch the server's ACME invalid email address error, and
152             # make a similar call when that happens
153             return get_email(more=True, invalid=(email != ""))
154     else:
155         return None
156 
157 
158 def choose_account(accounts):
159     """Choose an account.
160 
161     :param list accounts: Containing at least one
162         :class:`~letsencrypt.account.Account`
163 
164     """
165     # Note this will get more complicated once we start recording authorizations
166     labels = [acc.slug for acc in accounts]
167 
168     code, index = util(interfaces.IDisplay).menu(
169         "Please choose an account", labels)
170     if code == display_util.OK:
171         return accounts[index]
172     else:
173         return None
174 
175 
176 def choose_names(installer):
177     """Display screen to select domains to validate.
178 
179     :param installer: An installer object
180     :type installer: :class:`letsencrypt.interfaces.IInstaller`
181 
182     :returns: List of selected names
183     :rtype: `list` of `str`
184 
185     """
186     if installer is None:
187         logger.debug("No installer, picking names manually")
188         return _choose_names_manually()
189 
190     domains = list(installer.get_all_names())
191     names = get_valid_domains(domains)
192 
193     if not names:
194         manual = util(interfaces.IDisplay).yesno(
195             "No names were found in your configuration files.{0}You should "
196             "specify ServerNames in your config files in order to allow for "
197             "accurate installation of your certificate.{0}"
198             "If you do use the default vhost, you may specify the name "
199             "manually. Would you like to continue?{0}".format(os.linesep))
200 
201         if manual:
202             return _choose_names_manually()
203         else:
204             return []
205 
206     code, names = _filter_names(names)
207     if code == display_util.OK and names:
208         return names
209     else:
210         return []
211 
212 def get_valid_domains(domains):
213     """Helper method for choose_names that implements basic checks
214      on domain names
215 
216     :param list domains: Domain names to validate
217     :return: List of valid domains
218     :rtype: list
219     """
220     valid_domains = []
221     for domain in domains:
222         try:
223             le_util.check_domain_sanity(domain)
224             valid_domains.append(domain)
225         except errors.ConfigurationError:
226             continue
227     return valid_domains
228 
229 def _filter_names(names):
230     """Determine which names the user would like to select from a list.
231 
232     :param list names: domain names
233 
234     :returns: tuple of the form (`code`, `names`) where
235         `code` - str display exit code
236         `names` - list of names selected
237     :rtype: tuple
238 
239     """
240     code, names = util(interfaces.IDisplay).checklist(
241         "Which names would you like to activate HTTPS for?",
242         tags=names)
243     return code, [str(s) for s in names]
244 
245 
246 def _choose_names_manually():
247     """Manually input names for those without an installer."""
248 
249     code, input_ = util(interfaces.IDisplay).input(
250         "Please enter in your domain name(s) (comma and/or space separated) ")
251 
252     if code == display_util.OK:
253         return display_util.separate_list_input(input_)
254     return []
255 
256 
257 def success_installation(domains):
258     """Display a box confirming the installation of HTTPS.
259 
260     .. todo:: This should be centered on the screen
261 
262     :param list domains: domain names which were enabled
263 
264     """
265     util(interfaces.IDisplay).notification(
266         "Congratulations! You have successfully enabled {0}{1}{1}"
267         "You should test your configuration at:{1}{2}".format(
268             _gen_https_names(domains),
269             os.linesep,
270             os.linesep.join(_gen_ssl_lab_urls(domains))),
271         height=(10 + len(domains)),
272         pause=False)
273 
274 
275 def success_renewal(domains):
276     """Display a box confirming the renewal of an existing certificate.
277 
278     .. todo:: This should be centered on the screen
279 
280     :param list domains: domain names which were renewed
281 
282     """
283     util(interfaces.IDisplay).notification(
284         "Your existing certificate has been successfully renewed, and the "
285         "new certificate has been installed.{1}{1}"
286         "The new certificate covers the following domains: {0}{1}{1}"
287         "You should test your configuration at:{1}{2}".format(
288             _gen_https_names(domains),
289             os.linesep,
290             os.linesep.join(_gen_ssl_lab_urls(domains))),
291         height=(14 + len(domains)),
292         pause=False)
293 
294 
295 def _gen_ssl_lab_urls(domains):
296     """Returns a list of urls.
297 
298     :param list domains: Each domain is a 'str'
299 
300     """
301     return ["https://www.ssllabs.com/ssltest/analyze.html?d=%s" % dom for dom in domains]
302 
303 
304 def _gen_https_names(domains):
305     """Returns a string of the https domains.
306 
307     Domains are formatted nicely with https:// prepended to each.
308 
309     :param list domains: Each domain is a 'str'
310 
311     """
312     if len(domains) == 1:
313         return "https://{0}".format(domains[0])
314     elif len(domains) == 2:
315         return "https://{dom[0]} and https://{dom[1]}".format(dom=domains)
316     elif len(domains) > 2:
317         return "{0}{1}{2}".format(
318             ", ".join("https://%s" % dom for dom in domains[:-1]),
319             ", and https://",
320             domains[-1])
321 
322     return ""
323
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/letsencrypt/display/ops.py b/letsencrypt/display/ops.py
--- a/letsencrypt/display/ops.py
+++ b/letsencrypt/display/ops.py
@@ -123,6 +123,7 @@
         config, default, plugins, question,
         (interfaces.IAuthenticator, interfaces.IInstaller))
 
+
 def get_email(more=False, invalid=False):
     """Prompt for valid email address.
 
@@ -209,6 +210,7 @@
     else:
         return []
 
+
 def get_valid_domains(domains):
     """Helper method for choose_names that implements basic checks
      on domain names
@@ -226,6 +228,7 @@
             continue
     return valid_domains
 
+
 def _filter_names(names):
     """Determine which names the user would like to select from a list.
 
@@ -250,7 +253,41 @@
         "Please enter in your domain name(s) (comma and/or space separated) ")
 
     if code == display_util.OK:
-        return display_util.separate_list_input(input_)
+        invalid_domains = dict()
+        retry_message = ""
+        try:
+            domain_list = display_util.separate_list_input(input_)
+        except UnicodeEncodeError:
+            domain_list = []
+            retry_message = (
+                "Internationalized domain names are not presently "
+                "supported.{0}{0}Would you like to re-enter the "
+                "names?{0}").format(os.linesep)
+
+        for domain in domain_list:
+            try:
+                le_util.check_domain_sanity(domain)
+            except errors.ConfigurationError as e:
+                invalid_domains[domain] = e.message
+
+        if len(invalid_domains):
+            retry_message = (
+                "One or more of the entered domain names was not valid:"
+                "{0}{0}").format(os.linesep)
+            for domain in invalid_domains:
+                retry_message = retry_message + "{1}: {2}{0}".format(
+                    os.linesep, domain, invalid_domains[domain])
+            retry_message = retry_message + (
+                "{0}Would you like to re-enter the names?{0}").format(
+                    os.linesep)
+
+        if retry_message:
+            # We had error in input
+            retry = util(interfaces.IDisplay).yesno(retry_message)
+            if retry:
+                return _choose_names_manually()
+        else:
+            return domain_list
     return []

{"golden_diff": "diff --git a/letsencrypt/display/ops.py b/letsencrypt/display/ops.py\n--- a/letsencrypt/display/ops.py\n+++ b/letsencrypt/display/ops.py\n@@ -123,6 +123,7 @@\n         config, default, plugins, question,\n         (interfaces.IAuthenticator, interfaces.IInstaller))\n \n+\n def get_email(more=False, invalid=False):\n     \"\"\"Prompt for valid email address.\n \n@@ -209,6 +210,7 @@\n     else:\n         return []\n \n+\n def get_valid_domains(domains):\n     \"\"\"Helper method for choose_names that implements basic checks\n      on domain names\n@@ -226,6 +228,7 @@\n             continue\n     return valid_domains\n \n+\n def _filter_names(names):\n     \"\"\"Determine which names the user would like to select from a list.\n \n@@ -250,7 +253,41 @@\n         \"Please enter in your domain name(s) (comma and/or space separated) \")\n \n     if code == display_util.OK:\n-        return display_util.separate_list_input(input_)\n+        invalid_domains = dict()\n+        retry_message = \"\"\n+        try:\n+            domain_list = display_util.separate_list_input(input_)\n+        except UnicodeEncodeError:\n+            domain_list = []\n+            retry_message = (\n+                \"Internationalized domain names are not presently \"\n+                \"supported.{0}{0}Would you like to re-enter the \"\n+                \"names?{0}\").format(os.linesep)\n+\n+        for domain in domain_list:\n+            try:\n+                le_util.check_domain_sanity(domain)\n+            except errors.ConfigurationError as e:\n+                invalid_domains[domain] = e.message\n+\n+        if len(invalid_domains):\n+            retry_message = (\n+                \"One or more of the entered domain names was not valid:\"\n+                \"{0}{0}\").format(os.linesep)\n+            for domain in invalid_domains:\n+                retry_message = retry_message + \"{1}: {2}{0}\".format(\n+                    os.linesep, domain, invalid_domains[domain])\n+            retry_message = retry_message + (\n+                \"{0}Would you like to re-enter the names?{0}\").format(\n+                    os.linesep)\n+\n+        if retry_message:\n+            # We had error in input\n+            retry = util(interfaces.IDisplay).yesno(retry_message)\n+            if retry:\n+                return _choose_names_manually()\n+        else:\n+            return domain_list\n     return []\n", "issue": "Domain with german umlauts ('ascii' codec can't encode character)\nCentOS 7.1.1503\n\nI tried to create an certificat with domain contains german umlauts. Domain is: ent\u00e4.de\nI getting in the console this error:\n\n```\nAn unexpected error occurred:\nUnicodeEncodeError: 'ascii' codec can't encode character u'\\xe4' in position 3: ordinal not in range(128)\nPlease see the logfiles in /var/log/letsencrypt for more details.\n```\n\nLogfile:\n\n``` python\n2015-12-08 17:58:44,353:DEBUG:letsencrypt.cli:Exiting abnormally:\nTraceback (most recent call last):\n  File \"/root/.local/share/letsencrypt/bin/letsencrypt\", line 11, in <module>\n    sys.exit(main())\n  File \"/root/.local/share/letsencrypt/lib/python2.7/site-packages/letsencrypt/cli.py\", line 1283, in main\n    return args.func(args, config, plugins)\n  File \"/root/.local/share/letsencrypt/lib/python2.7/site-packages/letsencrypt/cli.py\", line 511, in obtain_cert\n    domains = _find_domains(args, installer)\n  File \"/root/.local/share/letsencrypt/lib/python2.7/site-packages/letsencrypt/cli.py\", line 106, in _find_domains\n    domains = display_ops.choose_names(installer)\n  File \"/root/.local/share/letsencrypt/lib/python2.7/site-packages/letsencrypt/display/ops.py\", line 187, in choose_names\n    return _choose_names_manually()\n  File \"/root/.local/share/letsencrypt/lib/python2.7/site-packages/letsencrypt/display/ops.py\", line 235, in _choose_names_manually\n    return display_util.separate_list_input(input_)\n  File \"/root/.local/share/letsencrypt/lib/python2.7/site-packages/letsencrypt/display/util.py\", line 419, in separate_list_input\n    return [str(string) for string in no_commas.split()]\nUnicodeEncodeError: 'ascii' codec can't encode character u'\\xe4' in position 3: ordinal not in range(128)\n```\n\nIs there a way to fix this?\n\n", "before_files": [{"content": "\"\"\"Contains UI methods for LE user operations.\"\"\"\nimport logging\nimport os\n\nimport zope.component\n\nfrom letsencrypt import errors\nfrom letsencrypt import interfaces\nfrom letsencrypt import le_util\nfrom letsencrypt.display import util as display_util\n\n\nlogger = logging.getLogger(__name__)\n\n# Define a helper function to avoid verbose code\nutil = zope.component.getUtility\n\n\ndef choose_plugin(prepared, question):\n    \"\"\"Allow the user to choose their plugin.\n\n    :param list prepared: List of `~.PluginEntryPoint`.\n    :param str question: Question to be presented to the user.\n\n    :returns: Plugin entry point chosen by the user.\n    :rtype: `~.PluginEntryPoint`\n\n    \"\"\"\n    opts = [plugin_ep.description_with_name +\n            (\" [Misconfigured]\" if plugin_ep.misconfigured else \"\")\n            for plugin_ep in prepared]\n\n    while True:\n        code, index = util(interfaces.IDisplay).menu(\n            question, opts, help_label=\"More Info\")\n\n        if code == display_util.OK:\n            plugin_ep = prepared[index]\n            if plugin_ep.misconfigured:\n                util(interfaces.IDisplay).notification(\n                    \"The selected plugin encountered an error while parsing \"\n                    \"your server configuration and cannot be used. The error \"\n                    \"was:\\n\\n{0}\".format(plugin_ep.prepare()),\n                    height=display_util.HEIGHT, pause=False)\n            else:\n                return plugin_ep\n        elif code == display_util.HELP:\n            if prepared[index].misconfigured:\n                msg = \"Reported Error: %s\" % prepared[index].prepare()\n            else:\n                msg = prepared[index].init().more_info()\n            util(interfaces.IDisplay).notification(\n                msg, height=display_util.HEIGHT)\n        else:\n            return None\n\n\ndef pick_plugin(config, default, plugins, question, ifaces):\n    \"\"\"Pick plugin.\n\n    :param letsencrypt.interfaces.IConfig: Configuration\n    :param str default: Plugin name supplied by user or ``None``.\n    :param letsencrypt.plugins.disco.PluginsRegistry plugins:\n        All plugins registered as entry points.\n    :param str question: Question to be presented to the user in case\n        multiple candidates are found.\n    :param list ifaces: Interfaces that plugins must provide.\n\n    :returns: Initialized plugin.\n    :rtype: IPlugin\n\n    \"\"\"\n    if default is not None:\n        # throw more UX-friendly error if default not in plugins\n        filtered = plugins.filter(lambda p_ep: p_ep.name == default)\n    else:\n        filtered = plugins.visible().ifaces(ifaces)\n\n    filtered.init(config)\n    verified = filtered.verify(ifaces)\n    verified.prepare()\n    prepared = verified.available()\n\n    if len(prepared) > 1:\n        logger.debug(\"Multiple candidate plugins: %s\", prepared)\n        plugin_ep = choose_plugin(prepared.values(), question)\n        if plugin_ep is None:\n            return None\n        else:\n            return plugin_ep.init()\n    elif len(prepared) == 1:\n        plugin_ep = prepared.values()[0]\n        logger.debug(\"Single candidate plugin: %s\", plugin_ep)\n        if plugin_ep.misconfigured:\n            return None\n        return plugin_ep.init()\n    else:\n        logger.debug(\"No candidate plugin\")\n        return None\n\n\ndef pick_authenticator(\n        config, default, plugins, question=\"How would you \"\n        \"like to authenticate with the Let's Encrypt CA?\"):\n    \"\"\"Pick authentication plugin.\"\"\"\n    return pick_plugin(\n        config, default, plugins, question, (interfaces.IAuthenticator,))\n\n\ndef pick_installer(config, default, plugins,\n                   question=\"How would you like to install certificates?\"):\n    \"\"\"Pick installer plugin.\"\"\"\n    return pick_plugin(\n        config, default, plugins, question, (interfaces.IInstaller,))\n\n\ndef pick_configurator(\n        config, default, plugins,\n        question=\"How would you like to authenticate and install \"\n                 \"certificates?\"):\n    \"\"\"Pick configurator plugin.\"\"\"\n    return pick_plugin(\n        config, default, plugins, question,\n        (interfaces.IAuthenticator, interfaces.IInstaller))\n\ndef get_email(more=False, invalid=False):\n    \"\"\"Prompt for valid email address.\n\n    :param bool more: explain why the email is strongly advisable, but how to\n        skip it\n    :param bool invalid: true if the user just typed something, but it wasn't\n        a valid-looking email\n\n    :returns: Email or ``None`` if cancelled by user.\n    :rtype: str\n\n    \"\"\"\n    msg = \"Enter email address (used for urgent notices and lost key recovery)\"\n    if invalid:\n        msg = \"There seem to be problems with that address. \" + msg\n    if more:\n        msg += ('\\n\\nIf you really want to skip this, you can run the client with '\n                '--register-unsafely-without-email but make sure you backup your '\n                'account key from /etc/letsencrypt/accounts\\n\\n')\n    code, email = zope.component.getUtility(interfaces.IDisplay).input(msg)\n\n    if code == display_util.OK:\n        if le_util.safe_email(email):\n            return email\n        else:\n            # TODO catch the server's ACME invalid email address error, and\n            # make a similar call when that happens\n            return get_email(more=True, invalid=(email != \"\"))\n    else:\n        return None\n\n\ndef choose_account(accounts):\n    \"\"\"Choose an account.\n\n    :param list accounts: Containing at least one\n        :class:`~letsencrypt.account.Account`\n\n    \"\"\"\n    # Note this will get more complicated once we start recording authorizations\n    labels = [acc.slug for acc in accounts]\n\n    code, index = util(interfaces.IDisplay).menu(\n        \"Please choose an account\", labels)\n    if code == display_util.OK:\n        return accounts[index]\n    else:\n        return None\n\n\ndef choose_names(installer):\n    \"\"\"Display screen to select domains to validate.\n\n    :param installer: An installer object\n    :type installer: :class:`letsencrypt.interfaces.IInstaller`\n\n    :returns: List of selected names\n    :rtype: `list` of `str`\n\n    \"\"\"\n    if installer is None:\n        logger.debug(\"No installer, picking names manually\")\n        return _choose_names_manually()\n\n    domains = list(installer.get_all_names())\n    names = get_valid_domains(domains)\n\n    if not names:\n        manual = util(interfaces.IDisplay).yesno(\n            \"No names were found in your configuration files.{0}You should \"\n            \"specify ServerNames in your config files in order to allow for \"\n            \"accurate installation of your certificate.{0}\"\n            \"If you do use the default vhost, you may specify the name \"\n            \"manually. Would you like to continue?{0}\".format(os.linesep))\n\n        if manual:\n            return _choose_names_manually()\n        else:\n            return []\n\n    code, names = _filter_names(names)\n    if code == display_util.OK and names:\n        return names\n    else:\n        return []\n\ndef get_valid_domains(domains):\n    \"\"\"Helper method for choose_names that implements basic checks\n     on domain names\n\n    :param list domains: Domain names to validate\n    :return: List of valid domains\n    :rtype: list\n    \"\"\"\n    valid_domains = []\n    for domain in domains:\n        try:\n            le_util.check_domain_sanity(domain)\n            valid_domains.append(domain)\n        except errors.ConfigurationError:\n            continue\n    return valid_domains\n\ndef _filter_names(names):\n    \"\"\"Determine which names the user would like to select from a list.\n\n    :param list names: domain names\n\n    :returns: tuple of the form (`code`, `names`) where\n        `code` - str display exit code\n        `names` - list of names selected\n    :rtype: tuple\n\n    \"\"\"\n    code, names = util(interfaces.IDisplay).checklist(\n        \"Which names would you like to activate HTTPS for?\",\n        tags=names)\n    return code, [str(s) for s in names]\n\n\ndef _choose_names_manually():\n    \"\"\"Manually input names for those without an installer.\"\"\"\n\n    code, input_ = util(interfaces.IDisplay).input(\n        \"Please enter in your domain name(s) (comma and/or space separated) \")\n\n    if code == display_util.OK:\n        return display_util.separate_list_input(input_)\n    return []\n\n\ndef success_installation(domains):\n    \"\"\"Display a box confirming the installation of HTTPS.\n\n    .. todo:: This should be centered on the screen\n\n    :param list domains: domain names which were enabled\n\n    \"\"\"\n    util(interfaces.IDisplay).notification(\n        \"Congratulations! You have successfully enabled {0}{1}{1}\"\n        \"You should test your configuration at:{1}{2}\".format(\n            _gen_https_names(domains),\n            os.linesep,\n            os.linesep.join(_gen_ssl_lab_urls(domains))),\n        height=(10 + len(domains)),\n        pause=False)\n\n\ndef success_renewal(domains):\n    \"\"\"Display a box confirming the renewal of an existing certificate.\n\n    .. todo:: This should be centered on the screen\n\n    :param list domains: domain names which were renewed\n\n    \"\"\"\n    util(interfaces.IDisplay).notification(\n        \"Your existing certificate has been successfully renewed, and the \"\n        \"new certificate has been installed.{1}{1}\"\n        \"The new certificate covers the following domains: {0}{1}{1}\"\n        \"You should test your configuration at:{1}{2}\".format(\n            _gen_https_names(domains),\n            os.linesep,\n            os.linesep.join(_gen_ssl_lab_urls(domains))),\n        height=(14 + len(domains)),\n        pause=False)\n\n\ndef _gen_ssl_lab_urls(domains):\n    \"\"\"Returns a list of urls.\n\n    :param list domains: Each domain is a 'str'\n\n    \"\"\"\n    return [\"https://www.ssllabs.com/ssltest/analyze.html?d=%s\" % dom for dom in domains]\n\n\ndef _gen_https_names(domains):\n    \"\"\"Returns a string of the https domains.\n\n    Domains are formatted nicely with https:// prepended to each.\n\n    :param list domains: Each domain is a 'str'\n\n    \"\"\"\n    if len(domains) == 1:\n        return \"https://{0}\".format(domains[0])\n    elif len(domains) == 2:\n        return \"https://{dom[0]} and https://{dom[1]}\".format(dom=domains)\n    elif len(domains) > 2:\n        return \"{0}{1}{2}\".format(\n            \", \".join(\"https://%s\" % dom for dom in domains[:-1]),\n            \", and https://\",\n            domains[-1])\n\n    return \"\"\n", "path": "letsencrypt/display/ops.py"}], "after_files": [{"content": "\"\"\"Contains UI methods for LE user operations.\"\"\"\nimport logging\nimport os\n\nimport zope.component\n\nfrom letsencrypt import errors\nfrom letsencrypt import interfaces\nfrom letsencrypt import le_util\nfrom letsencrypt.display import util as display_util\n\n\nlogger = logging.getLogger(__name__)\n\n# Define a helper function to avoid verbose code\nutil = zope.component.getUtility\n\n\ndef choose_plugin(prepared, question):\n    \"\"\"Allow the user to choose their plugin.\n\n    :param list prepared: List of `~.PluginEntryPoint`.\n    :param str question: Question to be presented to the user.\n\n    :returns: Plugin entry point chosen by the user.\n    :rtype: `~.PluginEntryPoint`\n\n    \"\"\"\n    opts = [plugin_ep.description_with_name +\n            (\" [Misconfigured]\" if plugin_ep.misconfigured else \"\")\n            for plugin_ep in prepared]\n\n    while True:\n        code, index = util(interfaces.IDisplay).menu(\n            question, opts, help_label=\"More Info\")\n\n        if code == display_util.OK:\n            plugin_ep = prepared[index]\n            if plugin_ep.misconfigured:\n                util(interfaces.IDisplay).notification(\n                    \"The selected plugin encountered an error while parsing \"\n                    \"your server configuration and cannot be used. The error \"\n                    \"was:\\n\\n{0}\".format(plugin_ep.prepare()),\n                    height=display_util.HEIGHT, pause=False)\n            else:\n                return plugin_ep\n        elif code == display_util.HELP:\n            if prepared[index].misconfigured:\n                msg = \"Reported Error: %s\" % prepared[index].prepare()\n            else:\n                msg = prepared[index].init().more_info()\n            util(interfaces.IDisplay).notification(\n                msg, height=display_util.HEIGHT)\n        else:\n            return None\n\n\ndef pick_plugin(config, default, plugins, question, ifaces):\n    \"\"\"Pick plugin.\n\n    :param letsencrypt.interfaces.IConfig: Configuration\n    :param str default: Plugin name supplied by user or ``None``.\n    :param letsencrypt.plugins.disco.PluginsRegistry plugins:\n        All plugins registered as entry points.\n    :param str question: Question to be presented to the user in case\n        multiple candidates are found.\n    :param list ifaces: Interfaces that plugins must provide.\n\n    :returns: Initialized plugin.\n    :rtype: IPlugin\n\n    \"\"\"\n    if default is not None:\n        # throw more UX-friendly error if default not in plugins\n        filtered = plugins.filter(lambda p_ep: p_ep.name == default)\n    else:\n        filtered = plugins.visible().ifaces(ifaces)\n\n    filtered.init(config)\n    verified = filtered.verify(ifaces)\n    verified.prepare()\n    prepared = verified.available()\n\n    if len(prepared) > 1:\n        logger.debug(\"Multiple candidate plugins: %s\", prepared)\n        plugin_ep = choose_plugin(prepared.values(), question)\n        if plugin_ep is None:\n            return None\n        else:\n            return plugin_ep.init()\n    elif len(prepared) == 1:\n        plugin_ep = prepared.values()[0]\n        logger.debug(\"Single candidate plugin: %s\", plugin_ep)\n        if plugin_ep.misconfigured:\n            return None\n        return plugin_ep.init()\n    else:\n        logger.debug(\"No candidate plugin\")\n        return None\n\n\ndef pick_authenticator(\n        config, default, plugins, question=\"How would you \"\n        \"like to authenticate with the Let's Encrypt CA?\"):\n    \"\"\"Pick authentication plugin.\"\"\"\n    return pick_plugin(\n        config, default, plugins, question, (interfaces.IAuthenticator,))\n\n\ndef pick_installer(config, default, plugins,\n                   question=\"How would you like to install certificates?\"):\n    \"\"\"Pick installer plugin.\"\"\"\n    return pick_plugin(\n        config, default, plugins, question, (interfaces.IInstaller,))\n\n\ndef pick_configurator(\n        config, default, plugins,\n        question=\"How would you like to authenticate and install \"\n                 \"certificates?\"):\n    \"\"\"Pick configurator plugin.\"\"\"\n    return pick_plugin(\n        config, default, plugins, question,\n        (interfaces.IAuthenticator, interfaces.IInstaller))\n\n\ndef get_email(more=False, invalid=False):\n    \"\"\"Prompt for valid email address.\n\n    :param bool more: explain why the email is strongly advisable, but how to\n        skip it\n    :param bool invalid: true if the user just typed something, but it wasn't\n        a valid-looking email\n\n    :returns: Email or ``None`` if cancelled by user.\n    :rtype: str\n\n    \"\"\"\n    msg = \"Enter email address (used for urgent notices and lost key recovery)\"\n    if invalid:\n        msg = \"There seem to be problems with that address. \" + msg\n    if more:\n        msg += ('\\n\\nIf you really want to skip this, you can run the client with '\n                '--register-unsafely-without-email but make sure you backup your '\n                'account key from /etc/letsencrypt/accounts\\n\\n')\n    code, email = zope.component.getUtility(interfaces.IDisplay).input(msg)\n\n    if code == display_util.OK:\n        if le_util.safe_email(email):\n            return email\n        else:\n            # TODO catch the server's ACME invalid email address error, and\n            # make a similar call when that happens\n            return get_email(more=True, invalid=(email != \"\"))\n    else:\n        return None\n\n\ndef choose_account(accounts):\n    \"\"\"Choose an account.\n\n    :param list accounts: Containing at least one\n        :class:`~letsencrypt.account.Account`\n\n    \"\"\"\n    # Note this will get more complicated once we start recording authorizations\n    labels = [acc.slug for acc in accounts]\n\n    code, index = util(interfaces.IDisplay).menu(\n        \"Please choose an account\", labels)\n    if code == display_util.OK:\n        return accounts[index]\n    else:\n        return None\n\n\ndef choose_names(installer):\n    \"\"\"Display screen to select domains to validate.\n\n    :param installer: An installer object\n    :type installer: :class:`letsencrypt.interfaces.IInstaller`\n\n    :returns: List of selected names\n    :rtype: `list` of `str`\n\n    \"\"\"\n    if installer is None:\n        logger.debug(\"No installer, picking names manually\")\n        return _choose_names_manually()\n\n    domains = list(installer.get_all_names())\n    names = get_valid_domains(domains)\n\n    if not names:\n        manual = util(interfaces.IDisplay).yesno(\n            \"No names were found in your configuration files.{0}You should \"\n            \"specify ServerNames in your config files in order to allow for \"\n            \"accurate installation of your certificate.{0}\"\n            \"If you do use the default vhost, you may specify the name \"\n            \"manually. Would you like to continue?{0}\".format(os.linesep))\n\n        if manual:\n            return _choose_names_manually()\n        else:\n            return []\n\n    code, names = _filter_names(names)\n    if code == display_util.OK and names:\n        return names\n    else:\n        return []\n\n\ndef get_valid_domains(domains):\n    \"\"\"Helper method for choose_names that implements basic checks\n     on domain names\n\n    :param list domains: Domain names to validate\n    :return: List of valid domains\n    :rtype: list\n    \"\"\"\n    valid_domains = []\n    for domain in domains:\n        try:\n            le_util.check_domain_sanity(domain)\n            valid_domains.append(domain)\n        except errors.ConfigurationError:\n            continue\n    return valid_domains\n\n\ndef _filter_names(names):\n    \"\"\"Determine which names the user would like to select from a list.\n\n    :param list names: domain names\n\n    :returns: tuple of the form (`code`, `names`) where\n        `code` - str display exit code\n        `names` - list of names selected\n    :rtype: tuple\n\n    \"\"\"\n    code, names = util(interfaces.IDisplay).checklist(\n        \"Which names would you like to activate HTTPS for?\",\n        tags=names)\n    return code, [str(s) for s in names]\n\n\ndef _choose_names_manually():\n    \"\"\"Manually input names for those without an installer.\"\"\"\n\n    code, input_ = util(interfaces.IDisplay).input(\n        \"Please enter in your domain name(s) (comma and/or space separated) \")\n\n    if code == display_util.OK:\n        invalid_domains = dict()\n        retry_message = \"\"\n        try:\n            domain_list = display_util.separate_list_input(input_)\n        except UnicodeEncodeError:\n            domain_list = []\n            retry_message = (\n                \"Internationalized domain names are not presently \"\n                \"supported.{0}{0}Would you like to re-enter the \"\n                \"names?{0}\").format(os.linesep)\n\n        for domain in domain_list:\n            try:\n                le_util.check_domain_sanity(domain)\n            except errors.ConfigurationError as e:\n                invalid_domains[domain] = e.message\n\n        if len(invalid_domains):\n            retry_message = (\n                \"One or more of the entered domain names was not valid:\"\n                \"{0}{0}\").format(os.linesep)\n            for domain in invalid_domains:\n                retry_message = retry_message + \"{1}: {2}{0}\".format(\n                    os.linesep, domain, invalid_domains[domain])\n            retry_message = retry_message + (\n                \"{0}Would you like to re-enter the names?{0}\").format(\n                    os.linesep)\n\n        if retry_message:\n            # We had error in input\n            retry = util(interfaces.IDisplay).yesno(retry_message)\n            if retry:\n                return _choose_names_manually()\n        else:\n            return domain_list\n    return []\n\n\ndef success_installation(domains):\n    \"\"\"Display a box confirming the installation of HTTPS.\n\n    .. todo:: This should be centered on the screen\n\n    :param list domains: domain names which were enabled\n\n    \"\"\"\n    util(interfaces.IDisplay).notification(\n        \"Congratulations! You have successfully enabled {0}{1}{1}\"\n        \"You should test your configuration at:{1}{2}\".format(\n            _gen_https_names(domains),\n            os.linesep,\n            os.linesep.join(_gen_ssl_lab_urls(domains))),\n        height=(10 + len(domains)),\n        pause=False)\n\n\ndef success_renewal(domains):\n    \"\"\"Display a box confirming the renewal of an existing certificate.\n\n    .. todo:: This should be centered on the screen\n\n    :param list domains: domain names which were renewed\n\n    \"\"\"\n    util(interfaces.IDisplay).notification(\n        \"Your existing certificate has been successfully renewed, and the \"\n        \"new certificate has been installed.{1}{1}\"\n        \"The new certificate covers the following domains: {0}{1}{1}\"\n        \"You should test your configuration at:{1}{2}\".format(\n            _gen_https_names(domains),\n            os.linesep,\n            os.linesep.join(_gen_ssl_lab_urls(domains))),\n        height=(14 + len(domains)),\n        pause=False)\n\n\ndef _gen_ssl_lab_urls(domains):\n    \"\"\"Returns a list of urls.\n\n    :param list domains: Each domain is a 'str'\n\n    \"\"\"\n    return [\"https://www.ssllabs.com/ssltest/analyze.html?d=%s\" % dom for dom in domains]\n\n\ndef _gen_https_names(domains):\n    \"\"\"Returns a string of the https domains.\n\n    Domains are formatted nicely with https:// prepended to each.\n\n    :param list domains: Each domain is a 'str'\n\n    \"\"\"\n    if len(domains) == 1:\n        return \"https://{0}\".format(domains[0])\n    elif len(domains) == 2:\n        return \"https://{dom[0]} and https://{dom[1]}\".format(dom=domains)\n    elif len(domains) > 2:\n        return \"{0}{1}{2}\".format(\n            \", \".join(\"https://%s\" % dom for dom in domains[:-1]),\n            \", and https://\",\n            domains[-1])\n\n    return \"\"\n", "path": "letsencrypt/display/ops.py"}]}