Hello community,

here is the log from the commit of package python-certbot for openSUSE:Factory 
checked in at 2019-10-02 14:56:05
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-certbot (Old)
 and      /work/SRC/openSUSE:Factory/.python-certbot.new.2352 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-certbot"

Wed Oct  2 14:56:05 2019 rev:18 rq:734565 version:0.39.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-certbot/python-certbot.changes    
2019-09-13 14:59:03.553279119 +0200
+++ /work/SRC/openSUSE:Factory/.python-certbot.new.2352/python-certbot.changes  
2019-10-02 14:56:06.151253827 +0200
@@ -1,0 +2,7 @@
+Wed Oct  2 10:02:37 UTC 2019 - Marketa Calabkova <[email protected]>
+
+- update to version 0.39.0
+  * Support for Python 3.8 was added to Certbot and all of its components.
+  * Don't send OCSP requests for expired certificates
+
+-------------------------------------------------------------------

Old:
----
  certbot-0.38.0.tar.gz

New:
----
  certbot-0.39.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-certbot.spec ++++++
--- /var/tmp/diff_new_pack.3o0GUS/_old  2019-10-02 14:56:06.991251624 +0200
+++ /var/tmp/diff_new_pack.3o0GUS/_new  2019-10-02 14:56:06.995251613 +0200
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-certbot
-Version:        0.38.0
+Version:        0.39.0
 Release:        0
 Summary:        ACME client
 License:        Apache-2.0

++++++ certbot-0.38.0.tar.gz -> certbot-0.39.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/CHANGELOG.md 
new/certbot-0.39.0/CHANGELOG.md
--- old/certbot-0.38.0/CHANGELOG.md     2019-09-03 21:42:35.000000000 +0200
+++ new/certbot-0.39.0/CHANGELOG.md     2019-10-01 21:48:40.000000000 +0200
@@ -2,11 +2,30 @@
 
 Certbot adheres to [Semantic Versioning](https://semver.org/).
 
+## 0.39.0 - 2019-10-01
+
+### Added
+
+* Support for Python 3.8 was added to Certbot and all of its components.
+* Support for CentOS 8 was added to certbot-auto.
+
+### Changed
+
+* Don't send OCSP requests for expired certificates
+* Return to using platform.linux_distribution instead of 
distro.linux_distribution in OS fingerprinting for Python < 3.8 
+* Updated the Nginx plugin's TLS configuration to keep support for some 
versions of IE11.
+
+### Fixed
+
+* Fixed OS detection in the Apache plugin on RHEL 6.
+
+More details about these changes can be found on our GitHub repo.
+
 ## 0.38.0 - 2019-09-03
 
 ### Added
 
-*
+* Disable session tickets for Nginx users when appropriate.
 
 ### Changed
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/PKG-INFO new/certbot-0.39.0/PKG-INFO
--- old/certbot-0.38.0/PKG-INFO 2019-09-03 21:42:38.000000000 +0200
+++ new/certbot-0.39.0/PKG-INFO 2019-10-01 21:48:41.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: certbot
-Version: 0.38.0
+Version: 0.39.0
 Summary: ACME client
 Home-page: https://github.com/letsencrypt/letsencrypt
 Author: Certbot Project
@@ -153,6 +153,7 @@
 Classifier: Programming Language :: Python :: 3.5
 Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
 Classifier: Topic :: Internet :: WWW/HTTP
 Classifier: Topic :: Security
 Classifier: Topic :: System :: Installation/Setup
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/__init__.py 
new/certbot-0.39.0/certbot/__init__.py
--- old/certbot-0.38.0/certbot/__init__.py      2019-09-03 21:42:37.000000000 
+0200
+++ new/certbot-0.39.0/certbot/__init__.py      2019-10-01 21:48:41.000000000 
+0200
@@ -1,4 +1,4 @@
 """Certbot client."""
 
 # version number like 1.2.3a0, must have at least 2 parts, like 1.2
-__version__ = '0.38.0'
+__version__ = '0.39.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/cert_manager.py 
new/certbot-0.39.0/certbot/cert_manager.py
--- old/certbot-0.38.0/certbot/cert_manager.py  2019-09-03 21:42:35.000000000 
+0200
+++ new/certbot-0.39.0/certbot/cert_manager.py  2019-10-01 21:48:40.000000000 
+0200
@@ -262,7 +262,7 @@
         reasons.append('TEST_CERT')
     if cert.target_expiry <= now:
         reasons.append('EXPIRED')
-    if checker.ocsp_revoked(cert.cert, cert.chain):
+    elif checker.ocsp_revoked(cert):
         reasons.append('REVOKED')
 
     if reasons:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/compat/filesystem.py 
new/certbot-0.39.0/certbot/compat/filesystem.py
--- old/certbot-0.38.0/certbot/compat/filesystem.py     2019-09-03 
21:42:35.000000000 +0200
+++ new/certbot-0.39.0/certbot/compat/filesystem.py     2019-10-01 
21:48:40.000000000 +0200
@@ -20,7 +20,7 @@
 else:
     POSIX_MODE = False
 
-from acme.magic_typing import List  # pylint: disable=unused-import, 
no-name-in-module
+from acme.magic_typing import List, Union, Tuple  # pylint: 
disable=unused-import, no-name-in-module
 
 
 def chmod(file_path, mode):
@@ -264,6 +264,7 @@
 
 
 def realpath(file_path):
+    # type: (str) -> str
     """
     Find the real path for the given path. This method resolves symlinks, 
including
     recursive symlinks, and is protected against symlinks that creates an 
infinite loop.
@@ -300,10 +301,11 @@
 # requires to be run under a privileged shell, so the user will always benefit
 # from the highest (privileged one) set of permissions on a given file.
 def is_executable(path):
+    # type: (str) -> bool
     """
     Is path an executable file?
     :param str path: path to test
-    :returns: True if path is an executable file
+    :return: True if path is an executable file
     :rtype: bool
     """
     if POSIX_MODE:
@@ -312,6 +314,118 @@
     return _win_is_executable(path)
 
 
+def has_world_permissions(path):
+    # type: (str) -> bool
+    """
+    Check if everybody/world has any right (read/write/execute) on a file 
given its path
+    :param str path: path to test
+    :return: True if everybody/world has any right to the file
+    :rtype: bool
+    """
+    if POSIX_MODE:
+        return bool(stat.S_IMODE(os.stat(path).st_mode) & stat.S_IRWXO)
+
+    security = win32security.GetFileSecurity(path, 
win32security.DACL_SECURITY_INFORMATION)
+    dacl = security.GetSecurityDescriptorDacl()
+
+    return bool(dacl.GetEffectiveRightsFromAcl({
+        'TrusteeForm': win32security.TRUSTEE_IS_SID,
+        'TrusteeType': win32security.TRUSTEE_IS_USER,
+        'Identifier': win32security.ConvertStringSidToSid('S-1-1-0'),
+    }))
+
+
+def compute_private_key_mode(old_key, base_mode):
+    # type: (str, int) -> int
+    """
+    Calculate the POSIX mode to apply to a private key given the previous 
private key
+    :param str old_key: path to the previous private key
+    :param int base_mode: the minimum modes to apply to a private key
+    :return: the POSIX mode to apply
+    :rtype: int
+    """
+    if POSIX_MODE:
+        # On Linux, we keep read/write/execute permissions
+        # for group and read permissions for everybody.
+        old_mode = (stat.S_IMODE(os.stat(old_key).st_mode) &
+                    (stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | 
stat.S_IROTH))
+        return base_mode | old_mode
+
+    # On Windows, the mode returned by os.stat is not reliable,
+    # so we do not keep any permission from the previous private key.
+    return base_mode
+
+
+def has_same_ownership(path1, path2):
+    # type: (str, str) -> bool
+    """
+    Return True if the ownership of two files given their respective path is 
the same.
+    On Windows, ownership is checked against owner only, since files do not 
have a group owner.
+    :param str path1: path to the first file
+    :param str path2: path to the second file
+    :return: True if both files have the same ownership, False otherwise
+    :rtype: bool
+
+    """
+    if POSIX_MODE:
+        stats1 = os.stat(path1)
+        stats2 = os.stat(path2)
+        return (stats1.st_uid, stats1.st_gid) == (stats2.st_uid, stats2.st_gid)
+
+    security1 = win32security.GetFileSecurity(path1, 
win32security.OWNER_SECURITY_INFORMATION)
+    user1 = security1.GetSecurityDescriptorOwner()
+
+    security2 = win32security.GetFileSecurity(path2, 
win32security.OWNER_SECURITY_INFORMATION)
+    user2 = security2.GetSecurityDescriptorOwner()
+
+    return user1 == user2
+
+
+def has_min_permissions(path, min_mode):
+    # type: (str, int) -> bool
+    """
+    Check if a file given its path has at least the permissions defined by the 
given minimal mode.
+    On Windows, group permissions are ignored since files do not have a group 
owner.
+    :param str path: path to the file to check
+    :param int min_mode: the minimal permissions expected
+    :return: True if the file matches the minimal permissions expectations, 
False otherwise
+    :rtype: bool
+    """
+    if POSIX_MODE:
+        st_mode = os.stat(path).st_mode
+        return st_mode == st_mode | min_mode
+
+    # Resolve symlinks, to get a consistent result with os.stat on Linux,
+    # that follows symlinks by default.
+    path = realpath(path)
+
+    # Get owner sid of the file
+    security = win32security.GetFileSecurity(
+        path, win32security.OWNER_SECURITY_INFORMATION | 
win32security.DACL_SECURITY_INFORMATION)
+    user = security.GetSecurityDescriptorOwner()
+    dacl = security.GetSecurityDescriptorDacl()
+    min_dacl = _generate_dacl(user, min_mode)
+
+    for index in range(min_dacl.GetAceCount()):
+        min_ace = min_dacl.GetAce(index)
+
+        # On a given ACE, index 0 is the ACE type, 1 is the permission mask, 
and 2 is the SID.
+        # See: http://timgolden.me.uk/pywin32-docs/PyACL__GetAce_meth.html
+        mask = min_ace[1]
+        user = min_ace[2]
+
+        effective_mask = dacl.GetEffectiveRightsFromAcl({
+            'TrusteeForm': win32security.TRUSTEE_IS_SID,
+            'TrusteeType': win32security.TRUSTEE_IS_USER,
+            'Identifier': user,
+        })
+
+        if effective_mask != effective_mask | mask:
+            return False
+
+    return True
+
+
 def _win_is_executable(path):
     if not os.path.isfile(path):
         return False
@@ -472,8 +586,8 @@
     This method compare the two given DACLs to check if they are identical.
     Identical means here that they contains the same set of ACEs in the same 
order.
     """
-    return ([dacl1.GetAce(index) for index in range(0, dacl1.GetAceCount())] ==
-            [dacl2.GetAce(index) for index in range(0, dacl2.GetAceCount())])
+    return ([dacl1.GetAce(index) for index in range(dacl1.GetAceCount())] ==
+            [dacl2.GetAce(index) for index in range(dacl2.GetAceCount())])
 
 
 def _get_current_user():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/compat/misc.py 
new/certbot-0.39.0/certbot/compat/misc.py
--- old/certbot-0.38.0/certbot/compat/misc.py   2019-09-03 21:42:35.000000000 
+0200
+++ new/certbot-0.39.0/certbot/compat/misc.py   2019-10-01 21:48:40.000000000 
+0200
@@ -5,7 +5,6 @@
 from __future__ import absolute_import
 
 import select
-import stat
 import sys
 
 try:
@@ -18,18 +17,6 @@
 from certbot.compat import os
 
 
-# MASK_FOR_PRIVATE_KEY_PERMISSIONS defines what are the permissions flags to 
keep
-# when transferring the permissions from an old private key to a new one.
-if POSIX_MODE:
-    # On Linux, we keep read/write/execute permissions
-    # for group and read permissions for everybody.
-    MASK_FOR_PRIVATE_KEY_PERMISSIONS = stat.S_IRGRP | stat.S_IWGRP | 
stat.S_IXGRP | stat.S_IROTH
-else:
-    # On Windows, the mode returned by os.stat is not reliable,
-    # so we do not keep any permission from the previous private key.
-    MASK_FOR_PRIVATE_KEY_PERMISSIONS = 0
-
-
 # For Linux: define OS specific standard binary directories
 STANDARD_BINARY_DIRS = ["/usr/sbin", "/usr/local/bin", "/usr/local/sbin"] if 
POSIX_MODE else []
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/compat/os.py 
new/certbot-0.39.0/certbot/compat/os.py
--- old/certbot-0.38.0/certbot/compat/os.py     2019-09-03 21:42:35.000000000 
+0200
+++ new/certbot-0.39.0/certbot/compat/os.py     2019-10-01 21:48:40.000000000 
+0200
@@ -116,3 +116,21 @@
     raise RuntimeError('Usage of os.access() is forbidden. '
                        'Use certbot.compat.filesystem.check_mode() or '
                        'certbot.compat.filesystem.is_executable() instead.')
+
+
+# On Windows os.stat call result is inconsistent, with a lot of flags that are 
not set or
+# meaningless. We need to use specialized functions from the 
certbot.compat.filesystem module.
+def stat(*unused_args, **unused_kwargs):
+    """Method os.stat() is forbidden"""
+    raise RuntimeError('Usage of os.stat() is forbidden. '
+                       'Use certbot.compat.filesystem functions instead '
+                       '(eg. has_min_permissions, has_same_ownership).')
+
+
+# Method os.fstat has the same problem than os.stat, since it is the same 
function,
+# but accepting a file descriptor instead of a path.
+def fstat(*unused_args, **unused_kwargs):
+    """Method os.stat() is forbidden"""
+    raise RuntimeError('Usage of os.fstat() is forbidden. '
+                       'Use certbot.compat.filesystem functions instead '
+                       '(eg. has_min_permissions, has_same_ownership).')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/lock.py 
new/certbot-0.39.0/certbot/lock.py
--- old/certbot-0.38.0/certbot/lock.py  2019-09-03 21:42:35.000000000 +0200
+++ new/certbot-0.39.0/certbot/lock.py  2019-10-01 21:48:40.000000000 +0200
@@ -167,14 +167,18 @@
         :returns: True if the lock was successfully acquired
         :rtype: bool
         """
+        # Normally os module should not be imported in certbot codebase except 
in certbot.compat
+        # for the sake of compatibility over Windows and Linux.
+        # We make an exception here, since _lock_success is private and called 
only on Linux.
+        from os import stat, fstat  # pylint: disable=os-module-forbidden
         try:
-            stat1 = os.stat(self._path)
+            stat1 = stat(self._path)
         except OSError as err:
             if err.errno == errno.ENOENT:
                 return False
             raise
 
-        stat2 = os.fstat(fd)
+        stat2 = fstat(fd)
         # If our locked file descriptor and the file on disk refer to
         # the same device and inode, they're the same file.
         return stat1.st_dev == stat2.st_dev and stat1.st_ino == stat2.st_ino
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/ocsp.py 
new/certbot-0.39.0/certbot/ocsp.py
--- old/certbot-0.38.0/certbot/ocsp.py  2019-09-03 21:42:35.000000000 +0200
+++ new/certbot-0.39.0/certbot/ocsp.py  2019-10-01 21:48:40.000000000 +0200
@@ -16,11 +16,13 @@
 from cryptography.hazmat.primitives import serialization
 from cryptography.hazmat.primitives import hashes  # type: ignore
 from cryptography.exceptions import UnsupportedAlgorithm, InvalidSignature
+import pytz
 import requests
 
 from acme.magic_typing import Optional, Tuple  # pylint: 
disable=unused-import, no-name-in-module
 from certbot import crypto_util
 from certbot import errors
+from certbot.storage import RenewableCert # pylint: disable=unused-import
 from certbot import util
 
 logger = logging.getLogger(__name__)
@@ -48,21 +50,29 @@
             else:
                 self.host_args = lambda host: ["Host", host]
 
-    def ocsp_revoked(self, cert_path, chain_path):
-        # type: (str, str) -> bool
+    def ocsp_revoked(self, cert):
+        # type: (RenewableCert) -> bool
         """Get revoked status for a particular cert version.
 
         .. todo:: Make this a non-blocking call
 
-        :param str cert_path: Path to certificate
-        :param str chain_path: Path to intermediate cert
-        :returns: True if revoked; False if valid or the check failed
+        :param `.storage.RenewableCert` cert: Certificate object
+        :returns: True if revoked; False if valid or the check failed or cert 
is expired.
         :rtype: bool
 
         """
+        cert_path, chain_path = cert.cert, cert.chain
+
         if self.broken:
             return False
 
+        # Let's Encrypt doesn't update OCSP for expired certificates,
+        # so don't check OCSP if the cert is expired.
+        # https://github.com/certbot/certbot/issues/7152
+        now = pytz.UTC.fromutc(datetime.utcnow())
+        if cert.target_expiry <= now:
+            return False
+
         url, host = _determine_ocsp_server(cert_path)
         if not host or not url:
             return False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/plugins/dns_common.py 
new/certbot-0.39.0/certbot/plugins/dns_common.py
--- old/certbot-0.38.0/certbot/plugins/dns_common.py    2019-09-03 
21:42:35.000000000 +0200
+++ new/certbot-0.39.0/certbot/plugins/dns_common.py    2019-10-01 
21:48:40.000000000 +0200
@@ -2,7 +2,6 @@
 
 import abc
 import logging
-import stat
 from time import sleep
 
 import configobj
@@ -12,6 +11,7 @@
 
 from certbot import errors
 from certbot import interfaces
+from certbot.compat import filesystem
 from certbot.compat import os
 from certbot.display import ops
 from certbot.display import util as display_util
@@ -312,8 +312,7 @@
 
     validate_file(filename)
 
-    permissions = stat.S_IMODE(os.stat(filename).st_mode)
-    if permissions & stat.S_IRWXO:
+    if filesystem.has_world_permissions(filename):
         logger.warning('Unsafe permissions on credentials configuration file: 
%s', filename)
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/plugins/dns_common_test.py 
new/certbot-0.39.0/certbot/plugins/dns_common_test.py
--- old/certbot-0.38.0/certbot/plugins/dns_common_test.py       2019-09-03 
21:42:35.000000000 +0200
+++ new/certbot-0.39.0/certbot/plugins/dns_common_test.py       2019-10-01 
21:48:40.000000000 +0200
@@ -7,14 +7,15 @@
 import mock
 
 from certbot import errors
+from certbot import util
 from certbot.compat import os
 from certbot.display import util as display_util
 from certbot.plugins import dns_common
 from certbot.plugins import dns_test_common
-from certbot.tests import util
+from certbot.tests import util as test_util
 
 
-class DNSAuthenticatorTest(util.TempDirTestCase, 
dns_test_common.BaseAuthenticatorTest):
+class DNSAuthenticatorTest(test_util.TempDirTestCase, 
dns_test_common.BaseAuthenticatorTest):
     # pylint: disable=protected-access
 
     class _FakeDNSAuthenticator(dns_common.DNSAuthenticator):
@@ -50,7 +51,7 @@
 
         self.auth._cleanup.assert_called_once_with(dns_test_common.DOMAIN, 
mock.ANY, mock.ANY)
 
-    @util.patch_get_utility()
+    @test_util.patch_get_utility()
     def test_prompt(self, mock_get_utility):
         mock_display = mock_get_utility()
         mock_display.input.side_effect = ((display_util.OK, "",),
@@ -59,14 +60,14 @@
         self.auth._configure("other_key", "")
         self.assertEqual(self.auth.config.fake_other_key, "value")
 
-    @util.patch_get_utility()
+    @test_util.patch_get_utility()
     def test_prompt_canceled(self, mock_get_utility):
         mock_display = mock_get_utility()
         mock_display.input.side_effect = ((display_util.CANCEL, "c",),)
 
         self.assertRaises(errors.PluginError, self.auth._configure, 
"other_key", "")
 
-    @util.patch_get_utility()
+    @test_util.patch_get_utility()
     def test_prompt_file(self, mock_get_utility):
         path = os.path.join(self.tempdir, 'file.ini')
         open(path, "wb").close()
@@ -80,7 +81,7 @@
         self.auth._configure_file("file_path", "")
         self.assertEqual(self.auth.config.fake_file_path, path)
 
-    @util.patch_get_utility()
+    @test_util.patch_get_utility()
     def test_prompt_file_canceled(self, mock_get_utility):
         mock_display = mock_get_utility()
         mock_display.directory_select.side_effect = ((display_util.CANCEL, 
"c",),)
@@ -96,7 +97,7 @@
 
         self.assertEqual(credentials.conf("test"), "value")
 
-    @util.patch_get_utility()
+    @test_util.patch_get_utility()
     def test_prompt_credentials(self, mock_get_utility):
         bad_path = os.path.join(self.tempdir, 'bad-file.ini')
         dns_test_common.write({"fake_other": "other_value"}, bad_path)
@@ -116,7 +117,7 @@
         self.assertEqual(credentials.conf("test"), "value")
 
 
-class CredentialsConfigurationTest(util.TempDirTestCase):
+class CredentialsConfigurationTest(test_util.TempDirTestCase):
     class _MockLoggingHandler(logging.Handler):
         messages = None
 
@@ -150,14 +151,14 @@
         dns_common.logger.addHandler(log)
 
         path = os.path.join(self.tempdir, 'too-permissive-file.ini')
-        open(path, "wb").close()
+        util.safe_open(path, "wb", 0o744).close()
 
         dns_common.CredentialsConfiguration(path)
 
         self.assertEqual(1, len([_ for _ in log.messages['warning'] if 
_.startswith("Unsafe")]))
 
 
-class CredentialsConfigurationRequireTest(util.TempDirTestCase):
+class CredentialsConfigurationRequireTest(test_util.TempDirTestCase):
 
     def setUp(self):
         super(CredentialsConfigurationRequireTest, self).setUp()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/plugins/webroot_test.py 
new/certbot-0.39.0/certbot/plugins/webroot_test.py
--- old/certbot-0.38.0/certbot/plugins/webroot_test.py  2019-09-03 
21:42:35.000000000 +0200
+++ new/certbot-0.39.0/certbot/plugins/webroot_test.py  2019-10-01 
21:48:40.000000000 +0200
@@ -34,7 +34,13 @@
 
     def setUp(self):
         from certbot.plugins.webroot import Authenticator
-        self.path = tempfile.mkdtemp()
+        # On Linux directories created by tempfile.mkdtemp inherit their 
permissions from their
+        # parent directory. So the actual permissions are inconsistent over 
various tests env.
+        # To circumvent this, a dedicated sub-workspace is created under the 
workspace, using
+        # filesystem.mkdir to get consistent permissions.
+        self.workspace = tempfile.mkdtemp()
+        self.path = os.path.join(self.workspace, 'webroot')
+        filesystem.mkdir(self.path)
         self.partial_root_challenge_path = os.path.join(
             self.path, ".well-known")
         self.root_challenge_path = os.path.join(
@@ -170,17 +176,12 @@
         self.assertTrue(filesystem.check_mode(self.validation_path, 0o644))
 
         # Check permissions of the directories
-
         for dirpath, dirnames, _ in os.walk(self.path):
             for directory in dirnames:
                 full_path = os.path.join(dirpath, directory)
                 self.assertTrue(filesystem.check_mode(full_path, 0o755))
 
-        parent_gid = os.stat(self.path).st_gid
-        parent_uid = os.stat(self.path).st_uid
-
-        self.assertEqual(os.stat(self.validation_path).st_gid, parent_gid)
-        self.assertEqual(os.stat(self.validation_path).st_uid, parent_uid)
+        self.assertTrue(filesystem.has_same_ownership(self.validation_path, 
self.path))
 
     def test_perform_cleanup(self):
         self.auth.prepare()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/storage.py 
new/certbot-0.39.0/certbot/storage.py
--- old/certbot-0.38.0/certbot/storage.py       2019-09-03 21:42:35.000000000 
+0200
+++ new/certbot-0.39.0/certbot/storage.py       2019-10-01 21:48:40.000000000 
+0200
@@ -18,7 +18,6 @@
 from certbot import error_handler
 from certbot import errors
 from certbot import util
-from certbot.compat import misc
 from certbot.compat import os
 from certbot.compat import filesystem
 from certbot.plugins import common as plugins_common
@@ -1107,9 +1106,7 @@
                 f.write(new_privkey)
             # Preserve gid and (mode & MASK_FOR_PRIVATE_KEY_PERMISSIONS)
             # from previous privkey in this lineage.
-            old_mode = (stat.S_IMODE(os.stat(old_privkey).st_mode) &
-                        misc.MASK_FOR_PRIVATE_KEY_PERMISSIONS)
-            mode = BASE_PRIVKEY_MODE | old_mode
+            mode = filesystem.compute_private_key_mode(old_privkey, 
BASE_PRIVKEY_MODE)
             filesystem.copy_ownership_and_apply_mode(
                 old_privkey, target["privkey"], mode, copy_user=False, 
copy_group=True)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/certbot-0.38.0/certbot/tests/compat/filesystem_test.py 
new/certbot-0.39.0/certbot/tests/compat/filesystem_test.py
--- old/certbot-0.38.0/certbot/tests/compat/filesystem_test.py  2019-09-03 
21:42:35.000000000 +0200
+++ new/certbot-0.39.0/certbot/tests/compat/filesystem_test.py  2019-10-01 
21:48:40.000000000 +0200
@@ -150,6 +150,24 @@
         
self.assertEqual(security_dacl.GetSecurityDescriptorDacl().GetAceCount(), 2)
 
 
+class ComputePrivateKeyModeTest(TempDirTestCase):
+    def setUp(self):
+        super(ComputePrivateKeyModeTest, self).setUp()
+        self.probe_path = _create_probe(self.tempdir)
+
+    def test_compute_private_key_mode(self):
+        filesystem.chmod(self.probe_path, 0o777)
+        new_mode = filesystem.compute_private_key_mode(self.probe_path, 0o600)
+
+        if POSIX_MODE:
+            # On Linux RWX permissions for group and R permission for world
+            # are persisted from the existing moe
+            self.assertEqual(new_mode, 0o674)
+        else:
+            # On Windows no permission is persisted
+            self.assertEqual(new_mode, 0o600)
+
+
 @unittest.skipIf(POSIX_MODE, reason='Tests specific to Windows security')
 class WindowsOpenTest(TempDirTestCase):
     def test_new_file_correct_permissions(self):
@@ -262,14 +280,14 @@
         self.assertEqual(original_mkdir, std_os.mkdir)
 
 
-class CopyOwnershipTest(test_util.TempDirTestCase):
-    """Tests about replacement of chown: copy_ownership_and_apply_mode"""
+class OwnershipTest(test_util.TempDirTestCase):
+    """Tests about copy_ownership_and_apply_mode and has_same_ownership"""
     def setUp(self):
-        super(CopyOwnershipTest, self).setUp()
+        super(OwnershipTest, self).setUp()
         self.probe_path = _create_probe(self.tempdir)
 
     @unittest.skipIf(POSIX_MODE, reason='Test specific to Windows security')
-    def test_windows(self):
+    def test_copy_ownership_windows(self):
         system = win32security.ConvertStringSidToSid(SYSTEM_SID)
         security = win32security.SECURITY_ATTRIBUTES().SECURITY_DESCRIPTOR
         security.SetSecurityDescriptorOwner(system, False)
@@ -295,7 +313,7 @@
                           if dacl.GetAce(index)[2] == everybody])
 
     @unittest.skipUnless(POSIX_MODE, reason='Test specific to Linux security')
-    def test_linux(self):
+    def test_copy_ownership_linux(self):
         with mock.patch('os.chown') as mock_chown:
             with mock.patch('os.chmod') as mock_chmod:
                 with mock.patch('os.stat') as mock_stat:
@@ -307,8 +325,18 @@
         mock_chown.assert_called_once_with(self.probe_path, 50, 51)
         mock_chmod.assert_called_once_with(self.probe_path, 0o700)
 
+    def test_has_same_ownership(self):
+        path1 = os.path.join(self.tempdir, 'test1')
+        path2 = os.path.join(self.tempdir, 'test2')
+
+        util.safe_open(path1, 'w').close()
+        util.safe_open(path2, 'w').close()
+
+        self.assertTrue(filesystem.has_same_ownership(path1, path2))
+
 
 class CheckPermissionsTest(test_util.TempDirTestCase):
+    """Tests relative to functions that check modes."""
     def setUp(self):
         super(CheckPermissionsTest, self).setUp()
         self.probe_path = _create_probe(self.tempdir)
@@ -353,6 +381,23 @@
             mock_owner.return_value = False
             self.assertFalse(filesystem.check_permissions(self.probe_path, 
0o744))
 
+    def test_check_min_permissions(self):
+        filesystem.chmod(self.probe_path, 0o744)
+        self.assertTrue(filesystem.has_min_permissions(self.probe_path, 0o744))
+
+        filesystem.chmod(self.probe_path, 0o700)
+        self.assertFalse(filesystem.has_min_permissions(self.probe_path, 
0o744))
+
+        filesystem.chmod(self.probe_path, 0o741)
+        self.assertFalse(filesystem.has_min_permissions(self.probe_path, 
0o744))
+
+    def test_is_world_reachable(self):
+        filesystem.chmod(self.probe_path, 0o744)
+        self.assertTrue(filesystem.has_world_permissions(self.probe_path))
+
+        filesystem.chmod(self.probe_path, 0o700)
+        self.assertFalse(filesystem.has_world_permissions(self.probe_path))
+
 
 class OsReplaceTest(test_util.TempDirTestCase):
     """Test to ensure consistent behavior of rename method"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/tests/compat/os_test.py 
new/certbot-0.39.0/certbot/tests/compat/os_test.py
--- old/certbot-0.38.0/certbot/tests/compat/os_test.py  2019-09-03 
21:42:35.000000000 +0200
+++ new/certbot-0.39.0/certbot/tests/compat/os_test.py  2019-10-01 
21:48:40.000000000 +0200
@@ -8,8 +8,8 @@
     """Unit tests for os module."""
     def test_forbidden_methods(self):
         # Checks for os module
-        for method in ['chmod', 'chown', 'open', 'mkdir',
-                       'makedirs', 'rename', 'replace', 'access']:
+        for method in ['chmod', 'chown', 'open', 'mkdir', 'makedirs', 'rename',
+                       'replace', 'access', 'stat', 'fstat']:
             self.assertRaises(RuntimeError, getattr(os, method))
         # Checks for os.path module
         for method in ['realpath']:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/tests/lock_test.py 
new/certbot-0.39.0/certbot/tests/lock_test.py
--- old/certbot-0.38.0/certbot/tests/lock_test.py       2019-09-03 
21:42:35.000000000 +0200
+++ new/certbot-0.39.0/certbot/tests/lock_test.py       2019-10-01 
21:48:40.000000000 +0200
@@ -82,7 +82,10 @@
         'Race conditions on lock are specific to the non-blocking file access 
approach on Linux.')
     def test_race(self):
         should_delete = [True, False]
-        stat = os.stat
+        # Normally os module should not be imported in certbot codebase except 
in certbot.compat
+        # for the sake of compatibility over Windows and Linux.
+        # We make an exception here, since test_race is a test function called 
only on Linux.
+        from os import stat  # pylint: disable=os-module-forbidden
 
         def delete_and_stat(path):
             """Wrap os.stat and maybe delete the file first."""
@@ -90,7 +93,7 @@
                 os.remove(path)
             return stat(path)
 
-        with mock.patch('certbot.lock.os.stat') as mock_stat:
+        with mock.patch('certbot.lock.filesystem.os.stat') as mock_stat:
             mock_stat.side_effect = delete_and_stat
             self._call(self.lock_path)
         self.assertFalse(should_delete)
@@ -117,7 +120,7 @@
 
     def test_unexpected_os_err(self):
         if POSIX_MODE:
-            mock_function = 'certbot.lock.os.stat'
+            mock_function = 'certbot.lock.filesystem.os.stat'
         else:
             mock_function = 'certbot.lock.msvcrt.locking'
         # The only expected errno are ENOENT and EACCES in lock module.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/tests/ocsp_test.py 
new/certbot-0.39.0/certbot/tests/ocsp_test.py
--- old/certbot-0.38.0/certbot/tests/ocsp_test.py       2019-09-03 
21:42:35.000000000 +0200
+++ new/certbot-0.39.0/certbot/tests/ocsp_test.py       2019-10-01 
21:48:40.000000000 +0200
@@ -16,6 +16,7 @@
 except (ImportError, AttributeError):  # pragma: no cover
     ocsp_lib = None  # type: ignore
 import mock
+import pytz
 
 from certbot import errors
 from certbot.tests import util as test_util
@@ -72,21 +73,34 @@
     @mock.patch('certbot.ocsp._determine_ocsp_server')
     @mock.patch('certbot.util.run_script')
     def test_ocsp_revoked(self, mock_run, mock_determine):
+        now = pytz.UTC.fromutc(datetime.utcnow())
+        cert_obj = mock.MagicMock()
+        cert_obj.cert = "x"
+        cert_obj.chain = "y"
+        cert_obj.target_expiry = now + timedelta(hours=2)
+
         self.checker.broken = True
         mock_determine.return_value = ("", "")
-        self.assertEqual(self.checker.ocsp_revoked("x", "y"), False)
+        self.assertEqual(self.checker.ocsp_revoked(cert_obj), False)
 
         self.checker.broken = False
         mock_run.return_value = tuple(openssl_happy[1:])
-        self.assertEqual(self.checker.ocsp_revoked("x", "y"), False)
+        self.assertEqual(self.checker.ocsp_revoked(cert_obj), False)
         self.assertEqual(mock_run.call_count, 0)
 
         mock_determine.return_value = ("http://x.co";, "x.co")
-        self.assertEqual(self.checker.ocsp_revoked("blah.pem", "chain.pem"), 
False)
+        self.assertEqual(self.checker.ocsp_revoked(cert_obj), False)
         mock_run.side_effect = errors.SubprocessError("Unable to load 
certificate launcher")
-        self.assertEqual(self.checker.ocsp_revoked("x", "y"), False)
+        self.assertEqual(self.checker.ocsp_revoked(cert_obj), False)
         self.assertEqual(mock_run.call_count, 2)
 
+        # cert expired
+        cert_obj.target_expiry = now
+        mock_determine.return_value = ("", "")
+        count_before = mock_determine.call_count
+        self.assertEqual(self.checker.ocsp_revoked(cert_obj), False)
+        self.assertEqual(mock_determine.call_count, count_before)
+
     def test_determine_ocsp_server(self):
         cert_path = test_util.vector_path('ocsp_certificate.pem')
 
@@ -131,18 +145,23 @@
         self.checker = ocsp.RevocationChecker()
         self.cert_path = test_util.vector_path('ocsp_certificate.pem')
         self.chain_path = test_util.vector_path('ocsp_issuer_certificate.pem')
+        self.cert_obj = mock.MagicMock()
+        self.cert_obj.cert = self.cert_path
+        self.cert_obj.chain = self.chain_path
+        now = pytz.UTC.fromutc(datetime.utcnow())
+        self.cert_obj.target_expiry = now + timedelta(hours=2)
 
     @mock.patch('certbot.ocsp._determine_ocsp_server')
     @mock.patch('certbot.ocsp._check_ocsp_cryptography')
     def test_ensure_cryptography_toggled(self, mock_revoke, mock_determine):
         mock_determine.return_value = ('http://example.com', 'example.com')
-        self.checker.ocsp_revoked(self.cert_path, self.chain_path)
+        self.checker.ocsp_revoked(self.cert_obj)
 
         mock_revoke.assert_called_once_with(self.cert_path, self.chain_path, 
'http://example.com')
 
     def test_revoke(self):
         with _ocsp_mock(ocsp_lib.OCSPCertStatus.REVOKED, 
ocsp_lib.OCSPResponseStatus.SUCCESSFUL):
-            revoked = self.checker.ocsp_revoked(self.cert_path, 
self.chain_path)
+            revoked = self.checker.ocsp_revoked(self.cert_obj)
         self.assertTrue(revoked)
 
     def test_responder_is_issuer(self):
@@ -152,7 +171,7 @@
         with _ocsp_mock(ocsp_lib.OCSPCertStatus.REVOKED,
                         ocsp_lib.OCSPResponseStatus.SUCCESSFUL) as mocks:
             mocks['mock_response'].return_value.responder_name = issuer.subject
-            self.checker.ocsp_revoked(self.cert_path, self.chain_path)
+            self.checker.ocsp_revoked(self.cert_obj)
         # Here responder and issuer are the same. So only the signature of the 
OCSP
         # response is checked (using the issuer/responder public key).
         self.assertEqual(mocks['mock_check'].call_count, 1)
@@ -167,7 +186,7 @@
 
         with _ocsp_mock(ocsp_lib.OCSPCertStatus.REVOKED,
                         ocsp_lib.OCSPResponseStatus.SUCCESSFUL) as mocks:
-            self.checker.ocsp_revoked(self.cert_path, self.chain_path)
+            self.checker.ocsp_revoked(self.cert_obj)
         # Here responder and issuer are not the same. Two signatures will be 
checked then,
         # first to verify the responder cert (using the issuer public key), 
second to
         # to verify the OCSP response itself (using the responder public key).
@@ -181,17 +200,17 @@
         # Server return an invalid HTTP response
         with _ocsp_mock(ocsp_lib.OCSPCertStatus.UNKNOWN, 
ocsp_lib.OCSPResponseStatus.SUCCESSFUL,
                         http_status_code=400):
-            revoked = self.checker.ocsp_revoked(self.cert_path, 
self.chain_path)
+            revoked = self.checker.ocsp_revoked(self.cert_obj)
         self.assertFalse(revoked)
 
         # OCSP response in invalid
         with _ocsp_mock(ocsp_lib.OCSPCertStatus.UNKNOWN, 
ocsp_lib.OCSPResponseStatus.UNAUTHORIZED):
-            revoked = self.checker.ocsp_revoked(self.cert_path, 
self.chain_path)
+            revoked = self.checker.ocsp_revoked(self.cert_obj)
         self.assertFalse(revoked)
 
         # OCSP response is valid, but certificate status is unknown
         with _ocsp_mock(ocsp_lib.OCSPCertStatus.UNKNOWN, 
ocsp_lib.OCSPResponseStatus.SUCCESSFUL):
-            revoked = self.checker.ocsp_revoked(self.cert_path, 
self.chain_path)
+            revoked = self.checker.ocsp_revoked(self.cert_obj)
         self.assertFalse(revoked)
 
         # The OCSP response says that the certificate is revoked, but 
certificate
@@ -200,32 +219,32 @@
             with 
mock.patch('cryptography.x509.Extensions.get_extension_for_class',
                             side_effect=x509.ExtensionNotFound(
                                 'Not found', 
x509.AuthorityInformationAccessOID.OCSP)):
-                revoked = self.checker.ocsp_revoked(self.cert_path, 
self.chain_path)
+                revoked = self.checker.ocsp_revoked(self.cert_obj)
         self.assertFalse(revoked)
 
         # OCSP response uses an unsupported signature.
         with _ocsp_mock(ocsp_lib.OCSPCertStatus.REVOKED, 
ocsp_lib.OCSPResponseStatus.SUCCESSFUL,
                         
check_signature_side_effect=UnsupportedAlgorithm('foo')):
-            revoked = self.checker.ocsp_revoked(self.cert_path, 
self.chain_path)
+            revoked = self.checker.ocsp_revoked(self.cert_obj)
         self.assertFalse(revoked)
 
         # OSCP signature response is invalid.
         with _ocsp_mock(ocsp_lib.OCSPCertStatus.REVOKED, 
ocsp_lib.OCSPResponseStatus.SUCCESSFUL,
                         check_signature_side_effect=InvalidSignature('foo')):
-            revoked = self.checker.ocsp_revoked(self.cert_path, 
self.chain_path)
+            revoked = self.checker.ocsp_revoked(self.cert_obj)
         self.assertFalse(revoked)
 
         # Assertion error on OCSP response validity
         with _ocsp_mock(ocsp_lib.OCSPCertStatus.REVOKED, 
ocsp_lib.OCSPResponseStatus.SUCCESSFUL,
                         check_signature_side_effect=AssertionError('foo')):
-            revoked = self.checker.ocsp_revoked(self.cert_path, 
self.chain_path)
+            revoked = self.checker.ocsp_revoked(self.cert_obj)
         self.assertFalse(revoked)
 
         # No responder cert in OCSP response
         with _ocsp_mock(ocsp_lib.OCSPCertStatus.REVOKED,
                         ocsp_lib.OCSPResponseStatus.SUCCESSFUL) as mocks:
             mocks['mock_response'].return_value.certificates = []
-            revoked = self.checker.ocsp_revoked(self.cert_path, 
self.chain_path)
+            revoked = self.checker.ocsp_revoked(self.cert_obj)
         self.assertFalse(revoked)
 
         # Responder cert is not signed by certificate issuer
@@ -234,7 +253,7 @@
             cert = mocks['mock_response'].return_value.certificates[0]
             mocks['mock_response'].return_value.certificates[0] = mock.Mock(
                 issuer='fake', subject=cert.subject)
-            revoked = self.checker.ocsp_revoked(self.cert_path, 
self.chain_path)
+            revoked = self.checker.ocsp_revoked(self.cert_obj)
         self.assertFalse(revoked)
 
         with _ocsp_mock(ocsp_lib.OCSPCertStatus.REVOKED, 
ocsp_lib.OCSPResponseStatus.SUCCESSFUL):
@@ -245,7 +264,7 @@
                 with 
mock.patch('cryptography.x509.Extensions.get_extension_for_class',
                                 side_effect=x509.ExtensionNotFound(
                                     'Not found', 
x509.AuthorityInformationAccessOID.OCSP)):
-                    revoked = self.checker.ocsp_revoked(self.cert_path, 
self.chain_path)
+                    revoked = self.checker.ocsp_revoked(self.cert_obj)
         self.assertFalse(revoked)
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/tests/util_test.py 
new/certbot-0.39.0/certbot/tests/util_test.py
--- old/certbot-0.38.0/certbot/tests/util_test.py       2019-09-03 
21:42:35.000000000 +0200
+++ new/certbot-0.39.0/certbot/tests/util_test.py       2019-10-01 
21:48:40.000000000 +0200
@@ -520,13 +520,16 @@
 
             with mock.patch('platform.system_alias',
                             return_value=('linux', '', '')):
-                with mock.patch('distro.linux_distribution',
-                                return_value=('', '', '')):
-                    self.assertEqual(get_python_os_info(), ("linux", ""))
+                with mock.patch('platform.linux_distribution',
+                                side_effect=AttributeError,
+                                create=True):
+                    with mock.patch('distro.linux_distribution',
+                                    return_value=('', '', '')):
+                        self.assertEqual(get_python_os_info(), ("linux", ""))
 
-                with mock.patch('distro.linux_distribution',
-                                return_value=('testdist', '42', '')):
-                    self.assertEqual(get_python_os_info(), ("testdist", "42"))
+                    with mock.patch('distro.linux_distribution',
+                                    return_value=('testdist', '42', '')):
+                        self.assertEqual(get_python_os_info(), ("testdist", 
"42"))
 
             with mock.patch('platform.system_alias',
                             return_value=('freebsd', '9.3-RC3-p1', '')):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot/util.py 
new/certbot-0.39.0/certbot/util.py
--- old/certbot-0.38.0/certbot/util.py  2019-09-03 21:42:35.000000000 +0200
+++ new/certbot-0.39.0/certbot/util.py  2019-10-01 21:48:40.000000000 +0200
@@ -392,7 +392,7 @@
     os_type, os_ver, _ = info
     os_type = os_type.lower()
     if os_type.startswith('linux'):
-        info = distro.linux_distribution()
+        info = _get_linux_distribution()
         # On arch, distro.linux_distribution() is reportedly ('','',''),
         # so handle it defensively
         if info[0]:
@@ -424,6 +424,14 @@
         os_ver = ''
     return os_type, os_ver
 
+def _get_linux_distribution():
+    """Gets the linux distribution name from the underlying OS"""
+
+    try:
+        return platform.linux_distribution()
+    except AttributeError:
+        return distro.linux_distribution()
+
 # Just make sure we don't get pwned... Make sure that it also doesn't
 # start with a period or have two consecutive periods <- this needs to
 # be done in addition to the regex
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/certbot.egg-info/PKG-INFO 
new/certbot-0.39.0/certbot.egg-info/PKG-INFO
--- old/certbot-0.38.0/certbot.egg-info/PKG-INFO        2019-09-03 
21:42:37.000000000 +0200
+++ new/certbot-0.39.0/certbot.egg-info/PKG-INFO        2019-10-01 
21:48:41.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: certbot
-Version: 0.38.0
+Version: 0.39.0
 Summary: ACME client
 Home-page: https://github.com/letsencrypt/letsencrypt
 Author: Certbot Project
@@ -153,6 +153,7 @@
 Classifier: Programming Language :: Python :: 3.5
 Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
 Classifier: Topic :: Internet :: WWW/HTTP
 Classifier: Topic :: Security
 Classifier: Topic :: System :: Installation/Setup
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/docs/cli-help.txt 
new/certbot-0.39.0/docs/cli-help.txt
--- old/certbot-0.38.0/docs/cli-help.txt        2019-09-03 21:42:35.000000000 
+0200
+++ new/certbot-0.39.0/docs/cli-help.txt        2019-10-01 21:48:40.000000000 
+0200
@@ -113,7 +113,7 @@
                         case, and to know when to deprecate support for past
                         Python versions and flags. If you wish to hide this
                         information from the Let's Encrypt server, set this to
-                        "". (default: CertbotACMEClient/0.37.2
+                        "". (default: CertbotACMEClient/0.38.0
                         (certbot(-auto); OS_NAME OS_VERSION) Authenticator/XXX
                         Installer/YYY (SUBCOMMAND; flags: FLAGS)
                         Py/major.minor.patchlevel). The flags encoded in the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/docs/using.rst 
new/certbot-0.39.0/docs/using.rst
--- old/certbot-0.38.0/docs/using.rst   2019-09-03 21:42:35.000000000 +0200
+++ new/certbot-0.39.0/docs/using.rst   2019-10-01 21:48:40.000000000 +0200
@@ -276,10 +276,8 @@
 gandi_             Y    N    Obtain certificates via the Gandi LiveDNS API
 varnish_           Y    N    Obtain certificates via a Varnish server
 external_          Y    N    A plugin for convenient scripting (See also 
ticket 2782_)
-icecast_           N    Y    Deploy certificates to Icecast 2 streaming media 
servers
 pritunl_           N    Y    Install certificates in pritunl distributed 
OpenVPN servers
 proxmox_           N    Y    Install certificates in Proxmox Virtualization 
servers
-heroku_            Y    Y    Integration with Heroku SSL
 dns-standalone_    Y    N    Obtain certificates via an integrated DNS server
 dns-ispconfig_     Y    N    DNS Authentication using ISPConfig as DNS server
 ================== ==== ==== 
===============================================================
@@ -287,13 +285,11 @@
 .. _haproxy: https://github.com/greenhost/certbot-haproxy
 .. _s3front: https://github.com/dlapiduz/letsencrypt-s3front
 .. _gandi: https://github.com/obynio/certbot-plugin-gandi
-.. _icecast: https://github.com/e00E/lets-encrypt-icecast
 .. _varnish: http://git.sesse.net/?p=letsencrypt-varnish-plugin
 .. _2782: https://github.com/certbot/certbot/issues/2782
 .. _pritunl: https://github.com/kharkevich/letsencrypt-pritunl
 .. _proxmox: https://github.com/kharkevich/letsencrypt-proxmox
 .. _external: https://github.com/marcan/letsencrypt-external
-.. _heroku: https://github.com/gboudreau/certbot-heroku
 .. _dns-standalone: https://github.com/siilike/certbot-dns-standalone
 .. _dns-ispconfig: https://github.com/m42e/certbot-dns-ispconfig
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.38.0/setup.py new/certbot-0.39.0/setup.py
--- old/certbot-0.38.0/setup.py 2019-09-03 21:42:36.000000000 +0200
+++ new/certbot-0.39.0/setup.py 2019-10-01 21:48:41.000000000 +0200
@@ -59,11 +59,17 @@
 # However environment markers are supported only with setuptools >= 36.2.
 # So this dependency is not added for old Linux distributions with old 
setuptools,
 # in order to allow these systems to build certbot from sources.
+pywin32_req = 'pywin32>=224'
 if StrictVersion(setuptools_version) >= StrictVersion('36.2'):
-    install_requires.append("pywin32>=224 ; sys_platform == 'win32'")
+    install_requires.append(pywin32_req + " ; sys_platform == 'win32'")
 elif 'bdist_wheel' in sys.argv[1:]:
     raise RuntimeError('Error, you are trying to build certbot wheels using an 
old version '
                        'of setuptools. Version 36.2+ of setuptools is 
required.')
+elif os.name == 'nt':
+    # This branch exists to improve this package's behavior on Windows. Without
+    # it, if the sdist is installed on Windows with an old version of
+    # setuptools, pywin32 will not be specified as a dependency.
+    install_requires.append(pywin32_req)
 
 dev_extras = [
     'astroid==1.6.5',
@@ -132,6 +138,7 @@
         'Programming Language :: Python :: 3.5',
         'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',
+        'Programming Language :: Python :: 3.8',
         'Topic :: Internet :: WWW/HTTP',
         'Topic :: Security',
         'Topic :: System :: Installation/Setup',


Reply via email to