/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5557.1.15 by John Arbash Meinel
Merge bzr.dev 5597 to resolve NEWS, aka bzr-2.3.txt
1
# Copyright (C) 2007, 2009, 2010, 2011 Canonical Ltd
2535.2.1 by Adeodato Simó
New SMTPConnection class, a reduced version of that in bzr-email.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2535.2.1 by Adeodato Simó
New SMTPConnection class, a reduced version of that in bzr-email.
16
17
from email.Message import Message
2694.2.1 by Aaron Bentley
Make error handling nicer when SMTP server not working
18
import errno
19
import smtplib
20
import socket
2535.2.1 by Adeodato Simó
New SMTPConnection class, a reduced version of that in bzr-email.
21
2694.2.1 by Aaron Bentley
Make error handling nicer when SMTP server not working
22
from bzrlib import (
23
    config,
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
24
    email_message,
2694.2.1 by Aaron Bentley
Make error handling nicer when SMTP server not working
25
    errors,
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
26
    smtp_connection,
27
    tests,
28
    ui,
2694.2.1 by Aaron Bentley
Make error handling nicer when SMTP server not working
29
    )
2535.2.1 by Adeodato Simó
New SMTPConnection class, a reduced version of that in bzr-email.
30
31
2694.2.1 by Aaron Bentley
Make error handling nicer when SMTP server not working
32
def connection_refuser():
33
    def connect(server):
34
        raise socket.error(errno.ECONNREFUSED, 'Connection Refused')
35
    smtp = smtplib.SMTP()
36
    smtp.connect = connect
37
    return smtp
38
39
2898.2.2 by James Henstridge
Fix test helper class naming, per John's review comments.
40
class StubSMTPFactory(object):
2898.2.1 by James Henstridge
Update SMTPConnection._create_connection to better follow the SMTP
41
    """A fake SMTP connection to test the connection setup."""
42
    def __init__(self, fail_on=None, smtp_features=None):
43
        self._fail_on = fail_on or []
44
        self._calls = []
45
        self._smtp_features = smtp_features or []
46
        self._ehlo_called = False
47
48
    def __call__(self):
49
        # The factory pretends to be a connection
50
        return self
51
52
    def connect(self, server):
53
        self._calls.append(('connect', server))
54
55
    def helo(self):
56
        self._calls.append(('helo',))
57
        if 'helo' in self._fail_on:
58
            return 500, 'helo failure'
59
        else:
60
            return 200, 'helo success'
61
62
    def ehlo(self):
63
        self._calls.append(('ehlo',))
64
        if 'ehlo' in self._fail_on:
65
            return 500, 'ehlo failure'
66
        else:
67
            self._ehlo_called = True
68
            return 200, 'ehlo success'
69
70
    def has_extn(self, extension):
71
        self._calls.append(('has_extn', extension))
72
        return self._ehlo_called and extension in self._smtp_features
73
74
    def starttls(self):
75
        self._calls.append(('starttls',))
76
        if 'starttls' in self._fail_on:
77
            return 500, 'starttls failure'
78
        else:
79
            self._ehlo_called = True
80
            return 200, 'starttls success'
81
82
2900.2.17 by Vincent Ladeuil
merge bzr.dev
83
class WideOpenSMTPFactory(StubSMTPFactory):
84
    """A fake smtp server that implements login by accepting anybody."""
85
86
    def login(self, user, password):
4147.1.1 by James Henstridge
Ensure that byte strings are passed to SMTP.login(), as passing unicode
87
        self._calls.append(('login', user, password))
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
88
89
6379.8.2 by Jelmer Vernooij
Update tests.
90
class StringStack(config.Stack):
91
92
    def __init__(self, text):
93
        store = config.IniFileStore()
94
        store._load_from_string(text)
95
        super(StringStack, self).__init__([store.get_sections])
96
97
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
98
class TestSMTPConnection(tests.TestCaseInTempDir):
2535.2.1 by Adeodato Simó
New SMTPConnection class, a reduced version of that in bzr-email.
99
2694.2.1 by Aaron Bentley
Make error handling nicer when SMTP server not working
100
    def get_connection(self, text, smtp_factory=None):
6379.8.2 by Jelmer Vernooij
Update tests.
101
        my_config = StringStack(text)
102
        return smtp_connection.SMTPConnection(
103
            my_config, _smtp_factory=smtp_factory)
2535.2.1 by Adeodato Simó
New SMTPConnection class, a reduced version of that in bzr-email.
104
105
    def test_defaults(self):
106
        conn = self.get_connection('')
107
        self.assertEqual('localhost', conn._smtp_server)
108
        self.assertEqual(None, conn._smtp_username)
109
        self.assertEqual(None, conn._smtp_password)
110
111
    def test_smtp_server(self):
112
        conn = self.get_connection('[DEFAULT]\nsmtp_server=host:10\n')
113
        self.assertEqual('host:10', conn._smtp_server)
114
2694.2.1 by Aaron Bentley
Make error handling nicer when SMTP server not working
115
    def test_missing_server(self):
116
        conn = self.get_connection('', smtp_factory=connection_refuser)
117
        self.assertRaises(errors.DefaultSMTPConnectionRefused, conn._connect)
118
        conn = self.get_connection('[DEFAULT]\nsmtp_server=smtp.example.com\n',
119
                                   smtp_factory=connection_refuser)
120
        self.assertRaises(errors.SMTPConnectionRefused, conn._connect)
121
2535.2.1 by Adeodato Simó
New SMTPConnection class, a reduced version of that in bzr-email.
122
    def test_smtp_username(self):
123
        conn = self.get_connection('')
124
        self.assertIs(None, conn._smtp_username)
125
126
        conn = self.get_connection('[DEFAULT]\nsmtp_username=joebody\n')
127
        self.assertEqual(u'joebody', conn._smtp_username)
128
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
129
    def test_smtp_password_from_config(self):
2535.2.1 by Adeodato Simó
New SMTPConnection class, a reduced version of that in bzr-email.
130
        conn = self.get_connection('')
131
        self.assertIs(None, conn._smtp_password)
132
133
        conn = self.get_connection('[DEFAULT]\nsmtp_password=mypass\n')
134
        self.assertEqual(u'mypass', conn._smtp_password)
135
2900.2.12 by Vincent Ladeuil
Since all schemes query AuthenticationConfig then prompt user, make that
136
    def test_smtp_password_from_user(self):
137
        user = 'joe'
138
        password = 'hispass'
2900.2.17 by Vincent Ladeuil
merge bzr.dev
139
        factory = WideOpenSMTPFactory()
2900.2.12 by Vincent Ladeuil
Since all schemes query AuthenticationConfig then prompt user, make that
140
        conn = self.get_connection('[DEFAULT]\nsmtp_username=%s\n' % user,
2900.2.17 by Vincent Ladeuil
merge bzr.dev
141
                                   smtp_factory=factory)
2900.2.12 by Vincent Ladeuil
Since all schemes query AuthenticationConfig then prompt user, make that
142
        self.assertIs(None, conn._smtp_password)
143
4449.3.40 by Martin Pool
Update SMTP tests to use CannedInputUIFactory
144
        ui.ui_factory = ui.CannedInputUIFactory([password])
2900.2.12 by Vincent Ladeuil
Since all schemes query AuthenticationConfig then prompt user, make that
145
        conn._connect()
146
        self.assertEqual(password, conn._smtp_password)
147
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
148
    def test_smtp_password_from_auth_config(self):
149
        user = 'joe'
150
        password = 'hispass'
2900.2.17 by Vincent Ladeuil
merge bzr.dev
151
        factory = WideOpenSMTPFactory()
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
152
        conn = self.get_connection('[DEFAULT]\nsmtp_username=%s\n' % user,
2900.2.17 by Vincent Ladeuil
merge bzr.dev
153
                                   smtp_factory=factory)
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
154
        self.assertEqual(user, conn._smtp_username)
155
        self.assertIs(None, conn._smtp_password)
156
        # Create a config file with the right password
157
        conf = config.AuthenticationConfig()
158
        conf._get_config().update({'smtptest':
159
                                       {'scheme': 'smtp', 'user':user,
160
                                        'password': password}})
161
        conf._save()
162
163
        conn._connect()
164
        self.assertEqual(password, conn._smtp_password)
165
4147.1.1 by James Henstridge
Ensure that byte strings are passed to SMTP.login(), as passing unicode
166
    def test_authenticate_with_byte_strings(self):
167
        user = 'joe'
5345.2.4 by Vincent Ladeuil
Fix fallouts, including an unclear test.
168
        unicode_pass = u'h\xECspass'
169
        utf8_pass = unicode_pass.encode('utf-8')
4147.1.1 by James Henstridge
Ensure that byte strings are passed to SMTP.login(), as passing unicode
170
        factory = WideOpenSMTPFactory()
171
        conn = self.get_connection(
6379.8.2 by Jelmer Vernooij
Update tests.
172
            '[DEFAULT]\nsmtp_username=%s\nsmtp_password=%s\n'
173
            % (user, utf8_pass), smtp_factory=factory)
5345.2.4 by Vincent Ladeuil
Fix fallouts, including an unclear test.
174
        self.assertEqual(unicode_pass, conn._smtp_password)
4147.1.1 by James Henstridge
Ensure that byte strings are passed to SMTP.login(), as passing unicode
175
        conn._connect()
176
        self.assertEqual([('connect', 'localhost'),
177
                          ('ehlo',),
178
                          ('has_extn', 'starttls'),
5345.2.4 by Vincent Ladeuil
Fix fallouts, including an unclear test.
179
                          ('login', user, utf8_pass)], factory._calls)
4147.1.2 by James Henstridge
Encode usernames and passwords as UTF-8 rather than ASCII. While
180
        smtp_username, smtp_password = factory._calls[-1][1:]
181
        self.assertIsInstance(smtp_username, str)
4147.1.1 by James Henstridge
Ensure that byte strings are passed to SMTP.login(), as passing unicode
182
        self.assertIsInstance(smtp_password, str)
183
2898.2.1 by James Henstridge
Update SMTPConnection._create_connection to better follow the SMTP
184
    def test_create_connection(self):
2898.2.2 by James Henstridge
Fix test helper class naming, per John's review comments.
185
        factory = StubSMTPFactory()
186
        conn = self.get_connection('', smtp_factory=factory)
2898.2.1 by James Henstridge
Update SMTPConnection._create_connection to better follow the SMTP
187
        conn._create_connection()
188
        self.assertEqual([('connect', 'localhost'),
189
                          ('ehlo',),
2898.2.2 by James Henstridge
Fix test helper class naming, per John's review comments.
190
                          ('has_extn', 'starttls')], factory._calls)
2898.2.1 by James Henstridge
Update SMTPConnection._create_connection to better follow the SMTP
191
192
    def test_create_connection_ehlo_fails(self):
193
        # Check that we call HELO if EHLO failed.
2898.2.2 by James Henstridge
Fix test helper class naming, per John's review comments.
194
        factory = StubSMTPFactory(fail_on=['ehlo'])
195
        conn = self.get_connection('', smtp_factory=factory)
2898.2.1 by James Henstridge
Update SMTPConnection._create_connection to better follow the SMTP
196
        conn._create_connection()
197
        self.assertEqual([('connect', 'localhost'),
198
                          ('ehlo',),
199
                          ('helo',),
2898.2.2 by James Henstridge
Fix test helper class naming, per John's review comments.
200
                          ('has_extn', 'starttls')], factory._calls)
2898.2.1 by James Henstridge
Update SMTPConnection._create_connection to better follow the SMTP
201
202
    def test_create_connection_ehlo_helo_fails(self):
203
        # Check that we raise an exception if both EHLO and HELO fail.
2898.2.2 by James Henstridge
Fix test helper class naming, per John's review comments.
204
        factory = StubSMTPFactory(fail_on=['ehlo', 'helo'])
205
        conn = self.get_connection('', smtp_factory=factory)
2898.2.1 by James Henstridge
Update SMTPConnection._create_connection to better follow the SMTP
206
        self.assertRaises(errors.SMTPError, conn._create_connection)
207
        self.assertEqual([('connect', 'localhost'),
208
                          ('ehlo',),
2898.2.2 by James Henstridge
Fix test helper class naming, per John's review comments.
209
                          ('helo',)], factory._calls)
2898.2.1 by James Henstridge
Update SMTPConnection._create_connection to better follow the SMTP
210
211
    def test_create_connection_starttls(self):
212
        # Check that STARTTLS plus a second EHLO are called if the
213
        # server says it supports the feature.
2898.2.2 by James Henstridge
Fix test helper class naming, per John's review comments.
214
        factory = StubSMTPFactory(smtp_features=['starttls'])
215
        conn = self.get_connection('', smtp_factory=factory)
2898.2.1 by James Henstridge
Update SMTPConnection._create_connection to better follow the SMTP
216
        conn._create_connection()
217
        self.assertEqual([('connect', 'localhost'),
218
                          ('ehlo',),
219
                          ('has_extn', 'starttls'),
220
                          ('starttls',),
2898.2.2 by James Henstridge
Fix test helper class naming, per John's review comments.
221
                          ('ehlo',)], factory._calls)
2898.2.1 by James Henstridge
Update SMTPConnection._create_connection to better follow the SMTP
222
223
    def test_create_connection_starttls_fails(self):
224
        # Check that we raise an exception if the server claims to
225
        # support STARTTLS, but then fails when we try to activate it.
2898.2.2 by James Henstridge
Fix test helper class naming, per John's review comments.
226
        factory = StubSMTPFactory(fail_on=['starttls'],
227
                                  smtp_features=['starttls'])
228
        conn = self.get_connection('', smtp_factory=factory)
2898.2.1 by James Henstridge
Update SMTPConnection._create_connection to better follow the SMTP
229
        self.assertRaises(errors.SMTPError, conn._create_connection)
230
        self.assertEqual([('connect', 'localhost'),
231
                          ('ehlo',),
232
                          ('has_extn', 'starttls'),
2898.2.2 by James Henstridge
Fix test helper class naming, per John's review comments.
233
                          ('starttls',)], factory._calls)
2898.2.1 by James Henstridge
Update SMTPConnection._create_connection to better follow the SMTP
234
2535.2.1 by Adeodato Simó
New SMTPConnection class, a reduced version of that in bzr-email.
235
    def test_get_message_addresses(self):
236
        msg = Message()
237
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
238
        from_, to = smtp_connection.SMTPConnection.get_message_addresses(msg)
2535.2.1 by Adeodato Simó
New SMTPConnection class, a reduced version of that in bzr-email.
239
        self.assertEqual('', from_)
240
        self.assertEqual([], to)
241
242
        msg['From'] = '"J. Random Developer" <jrandom@example.com>'
243
        msg['To'] = 'John Doe <john@doe.com>, Jane Doe <jane@doe.com>'
244
        msg['CC'] = u'Pepe P\xe9rez <pperez@ejemplo.com>'
245
        msg['Bcc'] = 'user@localhost'
246
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
247
        from_, to = smtp_connection.SMTPConnection.get_message_addresses(msg)
2535.2.1 by Adeodato Simó
New SMTPConnection class, a reduced version of that in bzr-email.
248
        self.assertEqual('jrandom@example.com', from_)
249
        self.assertEqual(sorted(['john@doe.com', 'jane@doe.com',
250
            'pperez@ejemplo.com', 'user@localhost']), sorted(to))
251
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
252
        # now with bzrlib's EmailMessage
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
253
        msg = email_message.EmailMessage(
254
            '"J. Random Developer" <jrandom@example.com>',
255
            ['John Doe <john@doe.com>', 'Jane Doe <jane@doe.com>',
256
             u'Pepe P\xe9rez <pperez@ejemplo.com>', 'user@localhost' ],
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
257
            'subject')
258
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
259
        from_, to = smtp_connection.SMTPConnection.get_message_addresses(msg)
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
260
        self.assertEqual('jrandom@example.com', from_)
261
        self.assertEqual(sorted(['john@doe.com', 'jane@doe.com',
262
            'pperez@ejemplo.com', 'user@localhost']), sorted(to))
2900.2.12 by Vincent Ladeuil
Since all schemes query AuthenticationConfig then prompt user, make that
263
2535.2.1 by Adeodato Simó
New SMTPConnection class, a reduced version of that in bzr-email.
264
    def test_destination_address_required(self):
265
        msg = Message()
266
        msg['From'] = '"J. Random Developer" <jrandom@example.com>'
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
267
        self.assertRaises(
268
            errors.NoDestinationAddress,
6379.8.2 by Jelmer Vernooij
Update tests.
269
            smtp_connection.SMTPConnection(StringStack("")).send_email, msg)
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
270
271
        msg = email_message.EmailMessage('from@from.com', '', 'subject')
272
        self.assertRaises(
273
            errors.NoDestinationAddress,
6379.8.2 by Jelmer Vernooij
Update tests.
274
            smtp_connection.SMTPConnection(StringStack("")).send_email, msg)
2900.2.11 by Vincent Ladeuil
Make smtp aware of authentication config.
275
276
        msg = email_message.EmailMessage('from@from.com', [], 'subject')
277
        self.assertRaises(
278
            errors.NoDestinationAddress,
6379.8.2 by Jelmer Vernooij
Update tests.
279
            smtp_connection.SMTPConnection(StringStack("")).send_email, msg)