Ticket #17471: patch_ticket_17471_c.txt

File patch_ticket_17471_c.txt, 8.1 KB (added by senko, 2 years ago)

Update to previous patch to suppress UnicodeDecodeError

Line 
1diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
2index bf0dea5..19258fb 100644
3--- a/django/conf/global_settings.py
4+++ b/django/conf/global_settings.py
5@@ -184,6 +184,7 @@ EMAIL_PORT = 25
6 EMAIL_HOST_USER = ''
7 EMAIL_HOST_PASSWORD = ''
8 EMAIL_USE_TLS = False
9+EMAIL_USE_SSL = False
10 
11 # List of strings representing installed apps.
12 INSTALLED_APPS = ()
13diff --git a/django/core/mail/backends/smtp.py b/django/core/mail/backends/smtp.py
14index e456b78..fc77080 100644
15--- a/django/core/mail/backends/smtp.py
16+++ b/django/core/mail/backends/smtp.py
17@@ -15,7 +15,7 @@ class EmailBackend(BaseEmailBackend):
18     A wrapper that manages the SMTP network connection.
19     """
20     def __init__(self, host=None, port=None, username=None, password=None,
21-                 use_tls=None, fail_silently=False, **kwargs):
22+                 use_tls=None, fail_silently=False, use_ssl=None, **kwargs):
23         super(EmailBackend, self).__init__(fail_silently=fail_silently)
24         self.host = host or settings.EMAIL_HOST
25         self.port = port or settings.EMAIL_PORT
26@@ -31,6 +31,13 @@ class EmailBackend(BaseEmailBackend):
27             self.use_tls = settings.EMAIL_USE_TLS
28         else:
29             self.use_tls = use_tls
30+        if use_ssl is None:
31+            self.use_ssl = settings.EMAIL_USE_SSL
32+        else:
33+            self.use_ssl = use_ssl
34+        if self.use_ssl and self.use_tls:
35+            raise ValueError(
36+                "EMAIL_USE_TLS/EMAIL_USE_SSL are mutually exclusive, so only set one of those settings to True.")
37         self.connection = None
38         self._lock = threading.RLock()
39 
40@@ -45,12 +52,18 @@ class EmailBackend(BaseEmailBackend):
41         try:
42             # If local_hostname is not specified, socket.getfqdn() gets used.
43             # For performance, we use the cached FQDN for local_hostname.
44-            self.connection = smtplib.SMTP(self.host, self.port,
45+            if self.use_ssl:
46+                self.connection = smtplib.SMTP_SSL(self.host, self.port,
47+                                           local_hostname=DNS_NAME.get_fqdn())
48+            else:
49+                self.connection = smtplib.SMTP(self.host, self.port,
50                                            local_hostname=DNS_NAME.get_fqdn())
51-            if self.use_tls:
52-                self.connection.ehlo()
53-                self.connection.starttls()
54-                self.connection.ehlo()
55+                # TLS/SSL are mutually exclusive, so only attempt TLS over
56+                # non-secure connections.
57+                if self.use_tls:
58+                    self.connection.ehlo()
59+                    self.connection.starttls()
60+                    self.connection.ehlo()
61             if self.username and self.password:
62                 self.connection.login(self.username, self.password)
63             return True
64diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
65index 30e7e84..350e8ec 100644
66--- a/docs/ref/settings.txt
67+++ b/docs/ref/settings.txt
68@@ -1055,6 +1055,26 @@ EMAIL_USE_TLS
69 Default: ``False``
70 
71 Whether to use a TLS (secure) connection when talking to the SMTP server.
72+This is used for explicit TLS connections, generally on port 587. If you are
73+experiencing hanging connections, see the implicit TLS setting
74+:setting:`EMAIL_USE_SSL`.
75+
76+.. setting:: EMAIL_USE_SSL
77+
78+EMAIL_USE_SSL
79+-------------
80+
81+.. versionadded:: 1.6
82+
83+Default: ``False``
84+
85+Whether to use an implicit TLS (secure) connection when talking to the SMTP
86+server. In most email documentation this type of TLS connection is referred
87+to as SSL. It is generally used on port 465. If you are experiencing problems,
88+see the explicit TLS setting :setting:`EMAIL_USE_TLS`.
89+
90+Note that :setting:`EMAIL_USE_TLS`/:setting:`EMAIL_USE_SSL` are mutually
91+exclusive, so only set one of those settings to ``True``.
92 
93 .. setting:: FILE_CHARSET
94 
95diff --git a/docs/topics/email.txt b/docs/topics/email.txt
96index b3d7254..8bf501c 100644
97--- a/docs/topics/email.txt
98+++ b/docs/topics/email.txt
99@@ -27,7 +27,8 @@ Mail is sent using the SMTP host and port specified in the
100 :setting:`EMAIL_HOST` and :setting:`EMAIL_PORT` settings. The
101 :setting:`EMAIL_HOST_USER` and :setting:`EMAIL_HOST_PASSWORD` settings, if
102 set, are used to authenticate to the SMTP server, and the
103-:setting:`EMAIL_USE_TLS` setting controls whether a secure connection is used.
104+:setting:`EMAIL_USE_TLS` and :setting:`EMAIL_USE_SSL` settings control whether
105+a secure connection is used.
106 
107 .. note::
108 
109@@ -408,8 +409,8 @@ SMTP backend
110 This is the default backend. Email will be sent through a SMTP server.
111 The server address and authentication credentials are set in the
112 :setting:`EMAIL_HOST`, :setting:`EMAIL_PORT`, :setting:`EMAIL_HOST_USER`,
113-:setting:`EMAIL_HOST_PASSWORD` and :setting:`EMAIL_USE_TLS` settings in your
114-settings file.
115+:setting:`EMAIL_HOST_PASSWORD`, :setting:`EMAIL_USE_TLS` and
116+:setting:`EMAIL_USE_SSL` settings in your settings file.
117 
118 The SMTP backend is the default configuration inherited by Django. If you
119 want to specify it explicitly, put the following in your settings::
120diff --git a/tests/mail/tests.py b/tests/mail/tests.py
121index c90dc7e..822c572 100644
122--- a/tests/mail/tests.py
123+++ b/tests/mail/tests.py
124@@ -9,6 +9,8 @@ import smtpd
125 import sys
126 import tempfile
127 import threading
128+from smtplib import SMTPException
129+from ssl import SSLError
130 
131 from django.core import mail
132 from django.core.mail import (EmailMessage, mail_admins, mail_managers,
133@@ -621,11 +623,23 @@ class ConsoleBackendTests(BaseEmailBackendTests, TestCase):
134         self.assertTrue(s.getvalue().startswith('Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nSubject: Subject\nFrom: from@example.com\nTo: to@example.com\nDate: '))
135 
136 
137+class FakeSMTPChannel(smtpd.SMTPChannel):
138+
139+    def collect_incoming_data(self, data):
140+        try:
141+            super(FakeSMTPChannel, self).collect_incoming_data(data)
142+        except UnicodeDecodeError:
143+            # ignore decode error in SSL/TLS connection tests as we only care
144+            # whether the connection attempt was made
145+            pass
146+
147+
148 class FakeSMTPServer(smtpd.SMTPServer, threading.Thread):
149     """
150     Asyncore SMTP server wrapped into a thread. Based on DummyFTPServer from:
151     http://svn.python.org/view/python/branches/py3k/Lib/test/test_ftplib.py?revision=86061&view=markup
152     """
153+    channel_class = FakeSMTPChannel
154 
155     def __init__(self, *args, **kwargs):
156         threading.Thread.__init__(self)
157@@ -738,3 +752,44 @@ class SMTPBackendTests(BaseEmailBackendTests, TestCase):
158             backend.close()
159         except Exception as e:
160             self.fail("close() unexpectedly raised an exception: %s" % e)
161+
162+    @override_settings(EMAIL_USE_TLS=True)
163+    def test_email_tls_use_settings(self):
164+        backend = smtp.EmailBackend()
165+        self.assertTrue(backend.use_tls)
166+
167+    @override_settings(EMAIL_USE_TLS=True)
168+    def test_email_tls_override_settings(self):
169+        backend = smtp.EmailBackend(use_tls=False)
170+        self.assertFalse(backend.use_tls)
171+
172+    def test_email_tls_default_disabled(self):
173+        backend = smtp.EmailBackend()
174+        self.assertFalse(backend.use_tls)
175+
176+    @override_settings(EMAIL_USE_SSL=True)
177+    def test_email_ssl_use_settings(self):
178+        backend = smtp.EmailBackend()
179+        self.assertTrue(backend.use_ssl)
180+
181+    @override_settings(EMAIL_USE_SSL=True)
182+    def test_email_ssl_override_settings(self):
183+        backend = smtp.EmailBackend(use_ssl=False)
184+        self.assertFalse(backend.use_ssl)
185+
186+    def test_email_ssl_default_disabled(self):
187+        backend = smtp.EmailBackend()
188+        self.assertFalse(backend.use_ssl)
189+
190+    @override_settings(EMAIL_USE_TLS=True)
191+    def test_email_tls_attempts_starttls(self):
192+        backend = smtp.EmailBackend()
193+        self.assertTrue(backend.use_tls)
194+        self.assertRaisesMessage(SMTPException,
195+            'STARTTLS extension not supported by server.', backend.open)
196+
197+    @override_settings(EMAIL_USE_SSL=True)
198+    def test_email_ssl_attempts_ssl_connection(self):
199+        backend = smtp.EmailBackend()
200+        self.assertTrue(backend.use_ssl)
201+        self.assertRaises(SSLError, backend.open)
Back to Top