# Copyright (C) 2012-2017 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
# GNU Mailman is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# GNU Mailman. If not, see .
"""Test the `confirm` command."""
import unittest
from datetime import datetime
from email.iterators import body_line_iterator
from mailman.app.lifecycle import create_list
from mailman.config import config
from mailman.database.transaction import transaction
from mailman.interfaces.subscriptions import ISubscriptionManager
from mailman.interfaces.usermanager import IUserManager
from mailman.runners.command import CommandRunner
from mailman.testing.helpers import (
get_queue_messages, make_testable_runner,
specialized_message_from_string as mfs)
from mailman.testing.layers import ConfigLayer
from zope.component import getUtility
class TestConfirm(unittest.TestCase):
"""Test confirmations."""
layer = ConfigLayer
def setUp(self):
self._commandq = config.switchboards['command']
self._runner = make_testable_runner(CommandRunner, 'command')
with transaction():
# Register a subscription requiring confirmation.
self._mlist = create_list('test@example.com')
self._mlist.send_welcome_message = False
anne = getUtility(IUserManager).create_address('anne@example.org')
registrar = ISubscriptionManager(self._mlist)
self._token, token_owner, member = registrar.register(anne)
def test_confirm_with_re_prefix(self):
subject = 'Re: confirm {}'.format(self._token)
msg = mfs("""\
From: anne@example.org
To: test-confirm@example.com
""")
msg['Subject'] = subject
self._commandq.enqueue(msg, dict(listid='test.example.com'))
self._runner.run()
# Anne is now a confirmed member so her user record and email address
# should exist in the database.
manager = getUtility(IUserManager)
user = manager.get_user('anne@example.org')
address = list(user.addresses)[0]
self.assertEqual(address.email, 'anne@example.org')
self.assertEqual(address.verified_on, datetime(2005, 8, 1, 7, 49, 23))
address = manager.get_address('anne@example.org')
self.assertEqual(address.email, 'anne@example.org')
def test_confirm_with_random_ascii_prefix(self):
subject = '\x99AW: confirm {}'.format(self._token)
msg = mfs("""\
From: anne@example.org
To: test-confirm@example.com
""")
msg['Subject'] = subject
self._commandq.enqueue(msg, dict(listid='test.example.com'))
self._runner.run()
# Anne is now a confirmed member so her user record and email address
# should exist in the database.
manager = getUtility(IUserManager)
user = manager.get_user('anne@example.org')
address = list(user.addresses)[0]
self.assertEqual(address.email, 'anne@example.org')
self.assertEqual(address.verified_on, datetime(2005, 8, 1, 7, 49, 23))
address = manager.get_address('anne@example.org')
self.assertEqual(address.email, 'anne@example.org')
def test_confirm_with_utf8_body(self):
# Clear out the virgin queue so that the test below only sees the
# reply to the confirmation message.
get_queue_messages('virgin')
subject = 'Re: confirm {}'.format(self._token)
to = 'test-confirm+{}@example.com'.format(self._token)
msg = mfs("""\
From: Anne Person
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
* test-confirm+90bf6ef335d92cfbbe540a5c9ebfecb107a48e48@example.com <=
test-confirm+90bf6ef335d92cfbbe540a5c9ebfecb107a48e48@example.com>:
> Email Address Registration Confirmation
>=20
> Hello, this is the GNU Mailman server at example.com.
>=20
> We have received a registration request for the email address
>=20
> anne@example.org
>=20
> Before you can start using GNU Mailman at this site, you must first con=
firm
> that this is your email address. You can do this by replying to this m=
essage,
> keeping the Subject header intact. Or you can visit this web page
>=20
> http://example.com/confirm/90bf6ef335d92cfbbe540a5c9ebfecb107a48e48
>=20
> If you do not wish to register this email address simply disregard this
> message. If you think you are being maliciously subscribed to the list=
, or
> have any other questions, you may contact
>=20
> postmaster@example.com
--=20
Franziskanerstra=C3=9Fe
""")
msg['Subject'] = subject
msg['To'] = to
self._commandq.enqueue(msg, dict(listid='test.example.com'))
self._runner.run()
# Anne is now a confirmed member so her user record and email address
# should exist in the database.
manager = getUtility(IUserManager)
user = manager.get_user('anne@example.org')
address = list(user.addresses)[0]
self.assertEqual(address.email, 'anne@example.org')
self.assertEqual(address.verified_on, datetime(2005, 8, 1, 7, 49, 23))
address = manager.get_address('anne@example.org')
self.assertEqual(address.email, 'anne@example.org')
items = get_queue_messages('virgin', expected_count=1)
self.assertEqual(items[0].msgdata['recipients'],
set(['anne@example.org']))
def test_confirm_with_no_command_in_utf8_body(self):
# Clear out the virgin queue so that the test below only sees the
# reply to the confirmation message.
get_queue_messages('virgin')
subject = 'Re: confirm {}'.format(self._token)
to = 'test-confirm+{}@example.com'.format(self._token)
msg = mfs("""\
From: Anne Person
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
Franziskanerstra=C3=9Fe
""")
msg['Subject'] = subject
msg['To'] = to
self._commandq.enqueue(msg, dict(listid='test.example.com'))
self._runner.run()
# Anne is now a confirmed member so her user record and email address
# should exist in the database.
manager = getUtility(IUserManager)
user = manager.get_user('anne@example.org')
address = list(user.addresses)[0]
self.assertEqual(address.email, 'anne@example.org')
self.assertEqual(address.verified_on, datetime(2005, 8, 1, 7, 49, 23))
address = manager.get_address('anne@example.org')
self.assertEqual(address.email, 'anne@example.org')
items = get_queue_messages('virgin', expected_count=1)
self.assertEqual(items[0].msgdata['recipients'],
set(['anne@example.org']))
def test_double_confirmation(self):
# 'confirm' in the Subject and in the To header should not try to
# confirm the token twice.
#
# Clear out the virgin queue so that the test below only sees the
# reply to the confirmation message.
get_queue_messages('virgin')
subject = 'Re: confirm {}'.format(self._token)
to = 'test-confirm+{}@example.com'.format(self._token)
msg = mfs("""\
From: Anne Person
""")
msg['Subject'] = subject
msg['To'] = to
self._commandq.enqueue(msg, dict(listid='test.example.com',
subaddress='confirm'))
self._runner.run()
# Anne is now a confirmed member so her user record and email address
# should exist in the database.
manager = getUtility(IUserManager)
user = manager.get_user('anne@example.org')
self.assertEqual(list(user.addresses)[0].email, 'anne@example.org')
# Make sure that the confirmation was not attempted twice.
items = get_queue_messages('virgin', expected_count=1)
# Search the contents of the results message. There should be just
# one 'Confirmation email' line.
confirmation_lines = []
in_results = False
for line in body_line_iterator(items[0].msg):
line = line.strip()
if in_results:
if line.startswith('- Done'):
break
if len(line) > 0:
confirmation_lines.append(line)
if line.strip() == '- Results:':
in_results = True
self.assertEqual(len(confirmation_lines), 1)
self.assertNotIn('did not match', confirmation_lines[0])
def test_welcome_message_after_confirmation(self):
# Confirmations with a welcome message set.
self._mlist.send_welcome_message = True
self._mlist.welcome_message_uri = 'mailman:///welcome.txt'
# 'confirm' in the Subject and in the To header should not try to
# confirm the token twice.
#
# Clear out the virgin queue so that the test below only sees the
# reply to the confirmation message.
get_queue_messages('virgin')
subject = 'Re: confirm {}'.format(self._token)
to = 'test-confirm+{}@example.com'.format(self._token)
msg = mfs("""\
From: Anne Person
""")
msg['Subject'] = subject
msg['To'] = to
self._commandq.enqueue(msg, dict(listid='test.example.com',
subaddress='confirm'))
self._runner.run()
# Now there's a email command notification and a welcome message. All
# we care about for this test is the welcome message.
items = get_queue_messages('virgin', sort_on='subject',
expected_count=2)
self.assertEqual(str(items[1].msg['subject']),
'Welcome to the "Test" mailing list')