/brz/remove-bazaar

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