From f44c2ff111a8961d295409186cc07aaf414c76bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= <mgorny@gentoo.org>
Date: Wed, 27 May 2020 13:23:37 +0200
Subject: [PATCH 1/4] Replace base64.*string() functions to fix py3.9 support

Replace base64.decodestring() and .encodestring() functions as they
were deprecated since Python 3.1 in favor of (equivalent) .decodebytes()
and .encodebytes(), and were eventually removed in Python 3.9.

While at it, replace most of their uses with base64.b64encode()
and .b64decode() that are preferable to the former wrt ticket #6446,
and they do not introduce line breaks that the twisted code usually
discarded.

Use .decodebytes() and .encodebytes() in DirDBM as it seems to rely
on the exact presence of newlines, and changing that would break
backwards compatibility.

Fixes: ticket:6446
Fixes: ticket:9831
---
 src/twisted/conch/scripts/tkconch.py          | 2 +-
 src/twisted/conch/test/test_keys.py           | 2 +-
 src/twisted/mail/pop3.py                      | 4 ++--
 src/twisted/mail/test/test_pop3.py            | 4 ++--
 src/twisted/persisted/dirdbm.py               | 4 ++--
 src/twisted/web/http.py                       | 2 +-
 src/twisted/web/test/test_http.py             | 6 +++---
 14 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/twisted/conch/scripts/tkconch.py b/src/twisted/conch/scripts/tkconch.py
index a662cabc8..744734343 100644
--- a/src/twisted/conch/scripts/tkconch.py
+++ b/src/twisted/conch/scripts/tkconch.py
@@ -412,7 +412,7 @@ class SSHClientTransport(transport.SSHClientTransport):
                 "known hosts.\r\n" %
                 (khHost, {b'ssh-dss':'DSA', b'ssh-rsa':'RSA'}[keyType]))
             with open(os.path.expanduser('~/.ssh/known_hosts'), 'a') as known_hosts:
-                encodedKey = base64.encodestring(pubKey).replace(b'\n', b'')
+                encodedKey = base64.b64encode(pubKey)
                 known_hosts.write('\n%s %s %s' % (khHost, keyType, encodedKey))
         except:
             log.deferr()
diff --git a/src/twisted/conch/test/test_keys.py b/src/twisted/conch/test/test_keys.py
index 650a19bfb..f76cbd1b4 100644
--- a/src/twisted/conch/test/test_keys.py
+++ b/src/twisted/conch/test/test_keys.py
@@ -404,7 +404,7 @@ SUrCyZXsNh6VXwjs3gKQ
 
         self.assertRaises(
             keys.BadKeyError,
-            keys.Key.fromString, data=b'{' + base64.encodestring(sexp) + b'}',
+            keys.Key.fromString, data=b'{' + base64.b64encode(sexp) + b'}',
             )
 
 
diff --git a/src/twisted/mail/pop3.py b/src/twisted/mail/pop3.py
index ffe9714c9..057389e3a 100644
--- a/src/twisted/mail/pop3.py
+++ b/src/twisted/mail/pop3.py
@@ -728,7 +728,7 @@ class POP3(basic.LineOnlyReceiver, policies.TimeoutMixin):
         self._auth = auth()
         chal = self._auth.getChallenge()
 
-        self.sendLine(b'+ ' + base64.encodestring(chal).rstrip(b'\n'))
+        self.sendLine(b'+ ' + base64.b64encode(chal))
         self.state = 'AUTH'
 
 
@@ -747,7 +747,7 @@ class POP3(basic.LineOnlyReceiver, policies.TimeoutMixin):
         """
         self.state = "COMMAND"
         try:
-            parts = base64.decodestring(line).split(None, 1)
+            parts = base64.b64decode(line).split(None, 1)
         except binascii.Error:
             self.failResponse(b"Invalid BASE64 encoding")
         else:
diff --git a/src/twisted/mail/test/test_pop3.py b/src/twisted/mail/test/test_pop3.py
index f7fbfaf1e..af335ab2d 100644
--- a/src/twisted/mail/test/test_pop3.py
+++ b/src/twisted/mail/test/test_pop3.py
@@ -1096,12 +1096,12 @@ class SASLTests(unittest.TestCase):
 
         p.lineReceived(b"AUTH CRAM-MD5")
         chal = s.getvalue().splitlines()[-1][2:]
-        chal = base64.decodestring(chal)
+        chal = base64.b64decode(chal)
         response = hmac.HMAC(b'testpassword', chal,
                              digestmod=md5).hexdigest().encode("ascii")
 
         p.lineReceived(
-            base64.encodestring(b'testuser ' + response).rstrip(b'\n'))
+            base64.b64encode(b'testuser ' + response))
         self.assertTrue(p.mbox)
         self.assertTrue(s.getvalue().splitlines()[-1].find(b"+OK") >= 0)
         p.connectionLost(failure.Failure(Exception("Test harness disconnect")))
diff --git a/src/twisted/persisted/dirdbm.py b/src/twisted/persisted/dirdbm.py
index 3ba7a59d4..7659ff765 100644
--- a/src/twisted/persisted/dirdbm.py
+++ b/src/twisted/persisted/dirdbm.py
@@ -77,14 +77,14 @@ class DirDBM:
         Encode a key so it can be used as a filename.
         """
         # NOTE: '_' is NOT in the base64 alphabet!
-        return base64.encodestring(k).replace(b'\n', b'_').replace(b"/", b"-")
+        return base64.encodebytes(k).replace(b'\n', b'_').replace(b"/", b"-")
 
 
     def _decode(self, k):
         """
         Decode a filename to get the key.
         """
-        return base64.decodestring(k.replace(b'_', b'\n').replace(b"-", b"/"))
+        return base64.decodebytes(k.replace(b'_', b'\n').replace(b"-", b"/"))
 
 
     def _readFile(self, path):
diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py
index 0e115741e..e9a080d21 100644
--- a/src/twisted/web/http.py
+++ b/src/twisted/web/http.py
@@ -1544,7 +1544,7 @@ class Request:
             bas, upw = authh.split()
             if bas.lower() != b"basic":
                 raise ValueError()
-            upw = base64.decodestring(upw)
+            upw = base64.b64decode(upw)
             self.user, self.password = upw.split(b':', 1)
         except (binascii.Error, ValueError):
             self.user = self.password = b''
diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py
index 112e56f46..02a4674a7 100644
--- a/src/twisted/web/test/test_http.py
+++ b/src/twisted/web/test/test_http.py
@@ -1604,7 +1604,7 @@ class ParsingTests(unittest.TestCase):
                 requests.append(self)
 
         for u, p in [(b"foo", b"bar"), (b"hello", b"there:z")]:
-            s = base64.encodestring(b":".join((u, p))).strip()
+            s = base64.b64encode(b":".join((u, p)))
             f = b"GET / HTTP/1.0\nAuthorization: Basic " + s + b"\n\n"
             self.runRequest(f, Request, 0)
             req = requests.pop()
@@ -2209,9 +2209,9 @@ Hello,
 
         u = b"foo"
         p = b"bar"
-        s = base64.encodestring(b":".join((u, p))).strip()
+        s = base64.b64encode(b":".join((u, p)))
         f = b"GET / HTTP/1.0\nAuthorization: Basic " + s + b"\n\n"
-        self.patch(base64, 'decodestring', lambda x: [])
+        self.patch(base64, 'b64decode', lambda x: [])
         self.runRequest(f, Request, 0)
         req = requests.pop()
         self.assertEqual((b'', b''), req.credentials)
-- 
2.26.2
