prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Install Python3 via EPEL on CentOS6 with letsencrypt-auto?
  CentOS 6 ships with Python 2.6 by default, which isn't 100% stably supported by letsencrypt, and which the Python community at large is trying to drop support for. But there's good news! CentOS has support for Software Collections which make it possible to install Python 2.7 in parallel: https://www.softwarecollections.org/en/scls/rhscl/python27/
  
  Would it make sense for `letsencrypt-auto` to just install a Python 2.7, in addition to all the other packages it provides, in order to a) ensure users always get a fully functional environment, b) help encourage folks to move to Python 2.7
  
  
  --- 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-auto-source/pieces/fetch.py`
  Content:
  ```
  1 """Do downloading and JSON parsing without additional dependencies. ::
  2 
  3     # Print latest released version of LE to stdout:
  4     python fetch.py --latest-version
  5 
  6     # Download letsencrypt-auto script from git tag v1.2.3 into the folder I'm
  7     # in, and make sure its signature verifies:
  8     python fetch.py --le-auto-script v1.2.3
  9 
  10 On failure, return non-zero.
  11 
  12 """
  13 
  14 from __future__ import print_function
  15 
  16 from distutils.version import LooseVersion
  17 from json import loads
  18 from os import devnull, environ
  19 from os.path import dirname, join
  20 import re
  21 from subprocess import check_call, CalledProcessError
  22 from sys import argv, exit
  23 from urllib2 import build_opener, HTTPHandler, HTTPSHandler
  24 from urllib2 import HTTPError, URLError
  25 
  26 PUBLIC_KEY = environ.get('LE_AUTO_PUBLIC_KEY', """-----BEGIN PUBLIC KEY-----
  27 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MR8W/galdxnpGqBsYbq
  28 OzQb2eyW15YFjDDEMI0ZOzt8f504obNs920lDnpPD2/KqgsfjOgw2K7xWDJIj/18
  29 xUvWPk3LDkrnokNiRkA3KOx3W6fHycKL+zID7zy+xZYBuh2fLyQtWV1VGQ45iNRp
  30 9+Zo7rH86cdfgkdnWTlNSHyTLW9NbXvyv/E12bppPcEvgCTAQXgnDVJ0/sqmeiij
  31 n9tTFh03aM+R2V/21h8aTraAS24qiPCz6gkmYGC8yr6mglcnNoYbsLNYZ69zF1XH
  32 cXPduCPdPdfLlzVlKK1/U7hkA28eG3BIAMh6uJYBRJTpiGgaGdPd7YekUB8S6cy+
  33 CQIDAQAB
  34 -----END PUBLIC KEY-----
  35 """)
  36 
  37 class ExpectedError(Exception):
  38     """A novice-readable exception that also carries the original exception for
  39     debugging"""
  40 
  41 
  42 class HttpsGetter(object):
  43     def __init__(self):
  44         """Build an HTTPS opener."""
  45         # Based on pip 1.4.1's URLOpener
  46         # This verifies certs on only Python >=2.7.9.
  47         self._opener = build_opener(HTTPSHandler())
  48         # Strip out HTTPHandler to prevent MITM spoof:
  49         for handler in self._opener.handlers:
  50             if isinstance(handler, HTTPHandler):
  51                 self._opener.handlers.remove(handler)
  52 
  53     def get(self, url):
  54         """Return the document contents pointed to by an HTTPS URL.
  55 
  56         If something goes wrong (404, timeout, etc.), raise ExpectedError.
  57 
  58         """
  59         try:
  60             # socket module docs say default timeout is None: that is, no
  61             # timeout
  62             return self._opener.open(url, timeout=30).read()
  63         except (HTTPError, IOError) as exc:
  64             raise ExpectedError("Couldn't download %s." % url, exc)
  65 
  66 
  67 def write(contents, dir, filename):
  68     """Write something to a file in a certain directory."""
  69     with open(join(dir, filename), 'w') as file:
  70         file.write(contents)
  71 
  72 
  73 def latest_stable_version(get):
  74     """Return the latest stable release of letsencrypt."""
  75     metadata = loads(get(
  76         environ.get('LE_AUTO_JSON_URL',
  77                     'https://pypi.python.org/pypi/certbot/json')))
  78     # metadata['info']['version'] actually returns the latest of any kind of
  79     # release release, contrary to https://wiki.python.org/moin/PyPIJSON.
  80     # The regex is a sufficient regex for picking out prereleases for most
  81     # packages, LE included.
  82     return str(max(LooseVersion(r) for r
  83                    in metadata['releases'].iterkeys()
  84                    if re.match('^[0-9.]+$', r)))
  85 
  86 
  87 def verified_new_le_auto(get, tag, temp_dir):
  88     """Return the path to a verified, up-to-date letsencrypt-auto script.
  89 
  90     If the download's signature does not verify or something else goes wrong
  91     with the verification process, raise ExpectedError.
  92 
  93     """
  94     le_auto_dir = environ.get(
  95         'LE_AUTO_DIR_TEMPLATE',
  96         'https://raw.githubusercontent.com/certbot/certbot/%s/'
  97         'letsencrypt-auto-source/') % tag
  98     write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto')
  99     write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig')
  100     write(PUBLIC_KEY, temp_dir, 'public_key.pem')
  101     try:
  102         with open(devnull, 'w') as dev_null:
  103             check_call(['openssl', 'dgst', '-sha256', '-verify',
  104                         join(temp_dir, 'public_key.pem'),
  105                         '-signature',
  106                         join(temp_dir, 'letsencrypt-auto.sig'),
  107                         join(temp_dir, 'letsencrypt-auto')],
  108                        stdout=dev_null,
  109                        stderr=dev_null)
  110     except CalledProcessError as exc:
  111         raise ExpectedError("Couldn't verify signature of downloaded "
  112                             "certbot-auto.", exc)
  113 
  114 
  115 def main():
  116     get = HttpsGetter().get
  117     flag = argv[1]
  118     try:
  119         if flag == '--latest-version':
  120             print(latest_stable_version(get))
  121         elif flag == '--le-auto-script':
  122             tag = argv[2]
  123             verified_new_le_auto(get, tag, dirname(argv[0]))
  124     except ExpectedError as exc:
  125         print(exc.args[0], exc.args[1])
  126         return 1
  127     else:
  128         return 0
  129 
  130 
  131 if __name__ == '__main__':
  132     exit(main())
  133
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/letsencrypt-auto-source/pieces/fetch.py b/letsencrypt-auto-source/pieces/fetch.py
  --- a/letsencrypt-auto-source/pieces/fetch.py
  +++ b/letsencrypt-auto-source/pieces/fetch.py
  @@ -11,17 +11,22 @@
   
   """
   
  -from __future__ import print_function
  +from __future__ import print_function, unicode_literals
   
   from distutils.version import LooseVersion
   from json import loads
   from os import devnull, environ
   from os.path import dirname, join
   import re
  +import ssl
   from subprocess import check_call, CalledProcessError
   from sys import argv, exit
  -from urllib2 import build_opener, HTTPHandler, HTTPSHandler
  -from urllib2 import HTTPError, URLError
  +try:
  +    from urllib2 import build_opener, HTTPHandler, HTTPSHandler
  +    from urllib2 import HTTPError, URLError
  +except ImportError:
  +    from urllib.request import build_opener, HTTPHandler, HTTPSHandler
  +    from urllib.error import HTTPError, URLError
   
   PUBLIC_KEY = environ.get('LE_AUTO_PUBLIC_KEY', """-----BEGIN PUBLIC KEY-----
   MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MR8W/galdxnpGqBsYbq
  @@ -43,8 +48,11 @@
       def __init__(self):
           """Build an HTTPS opener."""
           # Based on pip 1.4.1's URLOpener
  -        # This verifies certs on only Python >=2.7.9.
  -        self._opener = build_opener(HTTPSHandler())
  +        # This verifies certs on only Python >=2.7.9, and when NO_CERT_VERIFY isn't set.
  +        if environ.get('NO_CERT_VERIFY') == '1' and hasattr(ssl, 'SSLContext'):
  +            self._opener = build_opener(HTTPSHandler(context=create_CERT_NONE_context()))
  +        else:
  +            self._opener = build_opener(HTTPSHandler())
           # Strip out HTTPHandler to prevent MITM spoof:
           for handler in self._opener.handlers:
               if isinstance(handler, HTTPHandler):
  @@ -66,7 +74,7 @@
   
   def write(contents, dir, filename):
       """Write something to a file in a certain directory."""
  -    with open(join(dir, filename), 'w') as file:
  +    with open(join(dir, filename), 'wb') as file:
           file.write(contents)
   
   
  @@ -74,13 +82,13 @@
       """Return the latest stable release of letsencrypt."""
       metadata = loads(get(
           environ.get('LE_AUTO_JSON_URL',
  -                    'https://pypi.python.org/pypi/certbot/json')))
  +                    'https://pypi.python.org/pypi/certbot/json')).decode('UTF-8'))
       # metadata['info']['version'] actually returns the latest of any kind of
       # release release, contrary to https://wiki.python.org/moin/PyPIJSON.
       # The regex is a sufficient regex for picking out prereleases for most
       # packages, LE included.
       return str(max(LooseVersion(r) for r
  -                   in metadata['releases'].iterkeys()
  +                   in iter(metadata['releases'].keys())
                      if re.match('^[0-9.]+$', r)))
   
   
  @@ -97,7 +105,7 @@
           'letsencrypt-auto-source/') % tag
       write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto')
       write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig')
  -    write(PUBLIC_KEY, temp_dir, 'public_key.pem')
  +    write(PUBLIC_KEY.encode('UTF-8'), temp_dir, 'public_key.pem')
       try:
           with open(devnull, 'w') as dev_null:
               check_call(['openssl', 'dgst', '-sha256', '-verify',
  @@ -112,6 +120,14 @@
                               "certbot-auto.", exc)
   
   
  +def create_CERT_NONE_context():
  +    """Create a SSLContext object to not check hostname."""
  +    # PROTOCOL_TLS isn't available before 2.7.13 but this code is for 2.7.9+, so use this.
  +    context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
  +    context.verify_mode = ssl.CERT_NONE
  +    return context
  +
  +
   def main():
       get = HttpsGetter().get
       flag = argv[1]
  
verification_info: {"golden_diff": "diff --git a/letsencrypt-auto-source/pieces/fetch.py b/letsencrypt-auto-source/pieces/fetch.py\n--- a/letsencrypt-auto-source/pieces/fetch.py\n+++ b/letsencrypt-auto-source/pieces/fetch.py\n@@ -11,17 +11,22 @@\n \n \"\"\"\n \n-from __future__ import print_function\n+from __future__ import print_function, unicode_literals\n \n from distutils.version import LooseVersion\n from json import loads\n from os import devnull, environ\n from os.path import dirname, join\n import re\n+import ssl\n from subprocess import check_call, CalledProcessError\n from sys import argv, exit\n-from urllib2 import build_opener, HTTPHandler, HTTPSHandler\n-from urllib2 import HTTPError, URLError\n+try:\n+    from urllib2 import build_opener, HTTPHandler, HTTPSHandler\n+    from urllib2 import HTTPError, URLError\n+except ImportError:\n+    from urllib.request import build_opener, HTTPHandler, HTTPSHandler\n+    from urllib.error import HTTPError, URLError\n \n PUBLIC_KEY = environ.get('LE_AUTO_PUBLIC_KEY', \"\"\"-----BEGIN PUBLIC KEY-----\n MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MR8W/galdxnpGqBsYbq\n@@ -43,8 +48,11 @@\n     def __init__(self):\n         \"\"\"Build an HTTPS opener.\"\"\"\n         # Based on pip 1.4.1's URLOpener\n-        # This verifies certs on only Python >=2.7.9.\n-        self._opener = build_opener(HTTPSHandler())\n+        # This verifies certs on only Python >=2.7.9, and when NO_CERT_VERIFY isn't set.\n+        if environ.get('NO_CERT_VERIFY') == '1' and hasattr(ssl, 'SSLContext'):\n+            self._opener = build_opener(HTTPSHandler(context=create_CERT_NONE_context()))\n+        else:\n+            self._opener = build_opener(HTTPSHandler())\n         # Strip out HTTPHandler to prevent MITM spoof:\n         for handler in self._opener.handlers:\n             if isinstance(handler, HTTPHandler):\n@@ -66,7 +74,7 @@\n \n def write(contents, dir, filename):\n     \"\"\"Write something to a file in a certain directory.\"\"\"\n-    with open(join(dir, filename), 'w') as file:\n+    with open(join(dir, filename), 'wb') as file:\n         file.write(contents)\n \n \n@@ -74,13 +82,13 @@\n     \"\"\"Return the latest stable release of letsencrypt.\"\"\"\n     metadata = loads(get(\n         environ.get('LE_AUTO_JSON_URL',\n-                    'https://pypi.python.org/pypi/certbot/json')))\n+                    'https://pypi.python.org/pypi/certbot/json')).decode('UTF-8'))\n     # metadata['info']['version'] actually returns the latest of any kind of\n     # release release, contrary to https://wiki.python.org/moin/PyPIJSON.\n     # The regex is a sufficient regex for picking out prereleases for most\n     # packages, LE included.\n     return str(max(LooseVersion(r) for r\n-                   in metadata['releases'].iterkeys()\n+                   in iter(metadata['releases'].keys())\n                    if re.match('^[0-9.]+$', r)))\n \n \n@@ -97,7 +105,7 @@\n         'letsencrypt-auto-source/') % tag\n     write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto')\n     write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig')\n-    write(PUBLIC_KEY, temp_dir, 'public_key.pem')\n+    write(PUBLIC_KEY.encode('UTF-8'), temp_dir, 'public_key.pem')\n     try:\n         with open(devnull, 'w') as dev_null:\n             check_call(['openssl', 'dgst', '-sha256', '-verify',\n@@ -112,6 +120,14 @@\n                             \"certbot-auto.\", exc)\n \n \n+def create_CERT_NONE_context():\n+    \"\"\"Create a SSLContext object to not check hostname.\"\"\"\n+    # PROTOCOL_TLS isn't available before 2.7.13 but this code is for 2.7.9+, so use this.\n+    context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)\n+    context.verify_mode = ssl.CERT_NONE\n+    return context\n+\n+\n def main():\n     get = HttpsGetter().get\n     flag = argv[1]\n", "issue": "Install Python3 via EPEL on CentOS6 with letsencrypt-auto?\nCentOS 6 ships with Python 2.6 by default, which isn't 100% stably supported by letsencrypt, and which the Python community at large is trying to drop support for. But there's good news! CentOS has support for Software Collections which make it possible to install Python 2.7 in parallel: https://www.softwarecollections.org/en/scls/rhscl/python27/\n\nWould it make sense for `letsencrypt-auto` to just install a Python 2.7, in addition to all the other packages it provides, in order to a) ensure users always get a fully functional environment, b) help encourage folks to move to Python 2.7\n\n", "before_files": [{"content": "\"\"\"Do downloading and JSON parsing without additional dependencies. ::\n\n    # Print latest released version of LE to stdout:\n    python fetch.py --latest-version\n\n    # Download letsencrypt-auto script from git tag v1.2.3 into the folder I'm\n    # in, and make sure its signature verifies:\n    python fetch.py --le-auto-script v1.2.3\n\nOn failure, return non-zero.\n\n\"\"\"\n\nfrom __future__ import print_function\n\nfrom distutils.version import LooseVersion\nfrom json import loads\nfrom os import devnull, environ\nfrom os.path import dirname, join\nimport re\nfrom subprocess import check_call, CalledProcessError\nfrom sys import argv, exit\nfrom urllib2 import build_opener, HTTPHandler, HTTPSHandler\nfrom urllib2 import HTTPError, URLError\n\nPUBLIC_KEY = environ.get('LE_AUTO_PUBLIC_KEY', \"\"\"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MR8W/galdxnpGqBsYbq\nOzQb2eyW15YFjDDEMI0ZOzt8f504obNs920lDnpPD2/KqgsfjOgw2K7xWDJIj/18\nxUvWPk3LDkrnokNiRkA3KOx3W6fHycKL+zID7zy+xZYBuh2fLyQtWV1VGQ45iNRp\n9+Zo7rH86cdfgkdnWTlNSHyTLW9NbXvyv/E12bppPcEvgCTAQXgnDVJ0/sqmeiij\nn9tTFh03aM+R2V/21h8aTraAS24qiPCz6gkmYGC8yr6mglcnNoYbsLNYZ69zF1XH\ncXPduCPdPdfLlzVlKK1/U7hkA28eG3BIAMh6uJYBRJTpiGgaGdPd7YekUB8S6cy+\nCQIDAQAB\n-----END PUBLIC KEY-----\n\"\"\")\n\nclass ExpectedError(Exception):\n    \"\"\"A novice-readable exception that also carries the original exception for\n    debugging\"\"\"\n\n\nclass HttpsGetter(object):\n    def __init__(self):\n        \"\"\"Build an HTTPS opener.\"\"\"\n        # Based on pip 1.4.1's URLOpener\n        # This verifies certs on only Python >=2.7.9.\n        self._opener = build_opener(HTTPSHandler())\n        # Strip out HTTPHandler to prevent MITM spoof:\n        for handler in self._opener.handlers:\n            if isinstance(handler, HTTPHandler):\n                self._opener.handlers.remove(handler)\n\n    def get(self, url):\n        \"\"\"Return the document contents pointed to by an HTTPS URL.\n\n        If something goes wrong (404, timeout, etc.), raise ExpectedError.\n\n        \"\"\"\n        try:\n            # socket module docs say default timeout is None: that is, no\n            # timeout\n            return self._opener.open(url, timeout=30).read()\n        except (HTTPError, IOError) as exc:\n            raise ExpectedError(\"Couldn't download %s.\" % url, exc)\n\n\ndef write(contents, dir, filename):\n    \"\"\"Write something to a file in a certain directory.\"\"\"\n    with open(join(dir, filename), 'w') as file:\n        file.write(contents)\n\n\ndef latest_stable_version(get):\n    \"\"\"Return the latest stable release of letsencrypt.\"\"\"\n    metadata = loads(get(\n        environ.get('LE_AUTO_JSON_URL',\n                    'https://pypi.python.org/pypi/certbot/json')))\n    # metadata['info']['version'] actually returns the latest of any kind of\n    # release release, contrary to https://wiki.python.org/moin/PyPIJSON.\n    # The regex is a sufficient regex for picking out prereleases for most\n    # packages, LE included.\n    return str(max(LooseVersion(r) for r\n                   in metadata['releases'].iterkeys()\n                   if re.match('^[0-9.]+$', r)))\n\n\ndef verified_new_le_auto(get, tag, temp_dir):\n    \"\"\"Return the path to a verified, up-to-date letsencrypt-auto script.\n\n    If the download's signature does not verify or something else goes wrong\n    with the verification process, raise ExpectedError.\n\n    \"\"\"\n    le_auto_dir = environ.get(\n        'LE_AUTO_DIR_TEMPLATE',\n        'https://raw.githubusercontent.com/certbot/certbot/%s/'\n        'letsencrypt-auto-source/') % tag\n    write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto')\n    write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig')\n    write(PUBLIC_KEY, temp_dir, 'public_key.pem')\n    try:\n        with open(devnull, 'w') as dev_null:\n            check_call(['openssl', 'dgst', '-sha256', '-verify',\n                        join(temp_dir, 'public_key.pem'),\n                        '-signature',\n                        join(temp_dir, 'letsencrypt-auto.sig'),\n                        join(temp_dir, 'letsencrypt-auto')],\n                       stdout=dev_null,\n                       stderr=dev_null)\n    except CalledProcessError as exc:\n        raise ExpectedError(\"Couldn't verify signature of downloaded \"\n                            \"certbot-auto.\", exc)\n\n\ndef main():\n    get = HttpsGetter().get\n    flag = argv[1]\n    try:\n        if flag == '--latest-version':\n            print(latest_stable_version(get))\n        elif flag == '--le-auto-script':\n            tag = argv[2]\n            verified_new_le_auto(get, tag, dirname(argv[0]))\n    except ExpectedError as exc:\n        print(exc.args[0], exc.args[1])\n        return 1\n    else:\n        return 0\n\n\nif __name__ == '__main__':\n    exit(main())\n", "path": "letsencrypt-auto-source/pieces/fetch.py"}], "after_files": [{"content": "\"\"\"Do downloading and JSON parsing without additional dependencies. ::\n\n    # Print latest released version of LE to stdout:\n    python fetch.py --latest-version\n\n    # Download letsencrypt-auto script from git tag v1.2.3 into the folder I'm\n    # in, and make sure its signature verifies:\n    python fetch.py --le-auto-script v1.2.3\n\nOn failure, return non-zero.\n\n\"\"\"\n\nfrom __future__ import print_function, unicode_literals\n\nfrom distutils.version import LooseVersion\nfrom json import loads\nfrom os import devnull, environ\nfrom os.path import dirname, join\nimport re\nimport ssl\nfrom subprocess import check_call, CalledProcessError\nfrom sys import argv, exit\ntry:\n    from urllib2 import build_opener, HTTPHandler, HTTPSHandler\n    from urllib2 import HTTPError, URLError\nexcept ImportError:\n    from urllib.request import build_opener, HTTPHandler, HTTPSHandler\n    from urllib.error import HTTPError, URLError\n\nPUBLIC_KEY = environ.get('LE_AUTO_PUBLIC_KEY', \"\"\"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MR8W/galdxnpGqBsYbq\nOzQb2eyW15YFjDDEMI0ZOzt8f504obNs920lDnpPD2/KqgsfjOgw2K7xWDJIj/18\nxUvWPk3LDkrnokNiRkA3KOx3W6fHycKL+zID7zy+xZYBuh2fLyQtWV1VGQ45iNRp\n9+Zo7rH86cdfgkdnWTlNSHyTLW9NbXvyv/E12bppPcEvgCTAQXgnDVJ0/sqmeiij\nn9tTFh03aM+R2V/21h8aTraAS24qiPCz6gkmYGC8yr6mglcnNoYbsLNYZ69zF1XH\ncXPduCPdPdfLlzVlKK1/U7hkA28eG3BIAMh6uJYBRJTpiGgaGdPd7YekUB8S6cy+\nCQIDAQAB\n-----END PUBLIC KEY-----\n\"\"\")\n\nclass ExpectedError(Exception):\n    \"\"\"A novice-readable exception that also carries the original exception for\n    debugging\"\"\"\n\n\nclass HttpsGetter(object):\n    def __init__(self):\n        \"\"\"Build an HTTPS opener.\"\"\"\n        # Based on pip 1.4.1's URLOpener\n        # This verifies certs on only Python >=2.7.9, and when NO_CERT_VERIFY isn't set.\n        if environ.get('NO_CERT_VERIFY') == '1' and hasattr(ssl, 'SSLContext'):\n            self._opener = build_opener(HTTPSHandler(context=create_CERT_NONE_context()))\n        else:\n            self._opener = build_opener(HTTPSHandler())\n        # Strip out HTTPHandler to prevent MITM spoof:\n        for handler in self._opener.handlers:\n            if isinstance(handler, HTTPHandler):\n                self._opener.handlers.remove(handler)\n\n    def get(self, url):\n        \"\"\"Return the document contents pointed to by an HTTPS URL.\n\n        If something goes wrong (404, timeout, etc.), raise ExpectedError.\n\n        \"\"\"\n        try:\n            # socket module docs say default timeout is None: that is, no\n            # timeout\n            return self._opener.open(url, timeout=30).read()\n        except (HTTPError, IOError) as exc:\n            raise ExpectedError(\"Couldn't download %s.\" % url, exc)\n\n\ndef write(contents, dir, filename):\n    \"\"\"Write something to a file in a certain directory.\"\"\"\n    with open(join(dir, filename), 'wb') as file:\n        file.write(contents)\n\n\ndef latest_stable_version(get):\n    \"\"\"Return the latest stable release of letsencrypt.\"\"\"\n    metadata = loads(get(\n        environ.get('LE_AUTO_JSON_URL',\n                    'https://pypi.python.org/pypi/certbot/json')).decode('UTF-8'))\n    # metadata['info']['version'] actually returns the latest of any kind of\n    # release release, contrary to https://wiki.python.org/moin/PyPIJSON.\n    # The regex is a sufficient regex for picking out prereleases for most\n    # packages, LE included.\n    return str(max(LooseVersion(r) for r\n                   in iter(metadata['releases'].keys())\n                   if re.match('^[0-9.]+$', r)))\n\n\ndef verified_new_le_auto(get, tag, temp_dir):\n    \"\"\"Return the path to a verified, up-to-date letsencrypt-auto script.\n\n    If the download's signature does not verify or something else goes wrong\n    with the verification process, raise ExpectedError.\n\n    \"\"\"\n    le_auto_dir = environ.get(\n        'LE_AUTO_DIR_TEMPLATE',\n        'https://raw.githubusercontent.com/certbot/certbot/%s/'\n        'letsencrypt-auto-source/') % tag\n    write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto')\n    write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig')\n    write(PUBLIC_KEY.encode('UTF-8'), temp_dir, 'public_key.pem')\n    try:\n        with open(devnull, 'w') as dev_null:\n            check_call(['openssl', 'dgst', '-sha256', '-verify',\n                        join(temp_dir, 'public_key.pem'),\n                        '-signature',\n                        join(temp_dir, 'letsencrypt-auto.sig'),\n                        join(temp_dir, 'letsencrypt-auto')],\n                       stdout=dev_null,\n                       stderr=dev_null)\n    except CalledProcessError as exc:\n        raise ExpectedError(\"Couldn't verify signature of downloaded \"\n                            \"certbot-auto.\", exc)\n\n\ndef create_CERT_NONE_context():\n    \"\"\"Create a SSLContext object to not check hostname.\"\"\"\n    # PROTOCOL_TLS isn't available before 2.7.13 but this code is for 2.7.9+, so use this.\n    context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)\n    context.verify_mode = ssl.CERT_NONE\n    return context\n\n\ndef main():\n    get = HttpsGetter().get\n    flag = argv[1]\n    try:\n        if flag == '--latest-version':\n            print(latest_stable_version(get))\n        elif flag == '--le-auto-script':\n            tag = argv[2]\n            verified_new_le_auto(get, tag, dirname(argv[0]))\n    except ExpectedError as exc:\n        print(exc.args[0], exc.args[1])\n        return 1\n    else:\n        return 0\n\n\nif __name__ == '__main__':\n    exit(main())\n", "path": "letsencrypt-auto-source/pieces/fetch.py"}]}