예제 #1
0
    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 {0}'.format(self._token)
        to = 'test-confirm+{0}@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('*****@*****.**')
        address = list(user.addresses)[0]
        self.assertEqual(address.email, '*****@*****.**')
        self.assertEqual(address.verified_on, datetime(2005, 8, 1, 7, 49, 23))
        address = manager.get_address('*****@*****.**')
        self.assertEqual(address.email, '*****@*****.**')
        messages = get_queue_messages('virgin')
        self.assertEqual(len(messages), 1)
        self.assertEqual(messages[0].msgdata['recipients'],
                         set(['*****@*****.**']))
예제 #2
0
    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        self._member = add_member(self._mlist, '*****@*****.**',
                                  'Anne Person', 'xxx',
                                  DeliveryMode.regular, 'en')
        self._msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: You bounced
Message-ID: <first>

""")
        # Set up the translation context.
        self._var_dir = tempfile.mkdtemp()
        xx_template_path = os.path.join(
            self._var_dir, 'templates', 'site', 'xx', 'probe.txt')
        os.makedirs(os.path.dirname(xx_template_path))
        config.push('xx template dir', """\
        [paths.testing]
        var_dir: {0}
        """.format(self._var_dir))
        language_manager = getUtility(ILanguageManager)
        language_manager.add('xx', 'utf-8', 'Freedonia')
        self._member.preferences.preferred_language = 'xx'
        with open(xx_template_path, 'w') as fp:
            print("""\
blah blah blah
$listname
$address
$optionsurl
$owneraddr
""", file=fp)
        # Let assertMultiLineEqual work without bounds.
        self.maxDiff = None
예제 #3
0
    def test_send_one_digest_to_missing_fqdn_listname(self):
        msg = mfs("""\
To: [email protected]
From: [email protected]
Subject: message 1

""")
        self._handler.process(self._mlist, msg, {})
        del msg['subject']
        msg['subject'] = 'message 2'
        self._handler.process(self._mlist, msg, {})
        # There are no digests already being sent, but the ant mailing list
        # does have a digest mbox collecting messages.
        get_queue_messages('digest', expected_count=0)
        mailbox_path = os.path.join(self._mlist.data_path, 'digest.mmdf')
        self.assertGreater(os.path.getsize(mailbox_path), 0)
        args = FakeArgs()
        args.send = True
        args.lists.append('*****@*****.**')
        stderr = StringIO()
        with patch('mailman.commands.cli_digests.sys.stderr', stderr):
            self._command.process(args)
        self._runner.run()
        # The warning was printed to stderr.
        self.assertEqual(stderr.getvalue(),
                         'No such list found: [email protected]\n')
        # And no digest was prepared.
        self.assertGreater(os.path.getsize(mailbox_path), 0)
        get_queue_messages('virgin', expected_count=0)
예제 #4
0
    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        self._mlist.personalize = Personalization.individual
        # Make Anne a member of this mailing list.
        self._anne = add_member(self._mlist,
                                '*****@*****.**', 'Anne Person',
                                'xyz', DeliveryMode.regular, 'en')
        # Clear out any results from the previous test.
        del _deliveries[:]
        self._msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: test

""")
        # Set up a personalized footer for decoration.
        self._template_dir = tempfile.mkdtemp()
        path = os.path.join(self._template_dir,
                            'site', 'en', 'member-footer.txt')
        os.makedirs(os.path.dirname(path))
        with open(path, 'w') as fp:
            print("""\
address  : $user_address
delivered: $user_delivered_to
language : $user_language
name     : $user_name
options  : $user_optionsurl
""", file=fp)
        config.push('templates', """
        [paths.testing]
        template_dir: {0}
        """.format(self._template_dir))
        self._mlist.footer_uri = 'mailman:///member-footer.txt'
        # Let assertMultiLineEqual work without bounds.
        self.maxDiff = None
예제 #5
0
    def test_return_value(self):
        msg = mfs("""\
Message-ID: aardvark>

""")
        hash32 = add_message_hash(msg)
        self.assertEqual(hash32, '5KH3RA7ZM4VM6XOZXA7AST2XN2X4S3WY')
예제 #6
0
    def setUp(self):
        # Create a fake mailing list and message object.
        self._msg = mfs("""\
To: [email protected]
From: [email protected]
Subject: Testing the test list
Message-ID: <ant>
Message-ID-Hash: MS6QLWERIJLGCRF44J7USBFDELMNT2BW

Tests are better than no tests
but the water deserves to be swum.
""")
        with transaction():
            self._mlist = create_list('*****@*****.**')
        tempdir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, tempdir)
        # Here's the command to execute our fake MHonArc process.
        shutil.copy(
            resource_filename('mailman.archiving.tests', 'fake_mhonarc.py'),
            tempdir)
        self._output_file = os.path.join(tempdir, 'output.txt')
        command = '{} {} {}'.format(
            sys.executable,
            os.path.join(tempdir, 'fake_mhonarc.py'),
            self._output_file)
        # Write an external configuration file which points the command at our
        # fake MHonArc process.
        self._cfg = os.path.join(tempdir, 'mhonarc.cfg')
        with open(self._cfg, 'w', encoding='utf-8') as fp:
            print("""\
[general]
base_url: http://$hostname/archives/$fqdn_listname
command: {command}
""".format(command=command), file=fp)
예제 #7
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 {0}'.format(self._token)
        to = 'test-confirm+{0}@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.
        messages = get_queue_messages('virgin', sort_on='subject')
        self.assertEqual(len(messages), 2)
        message = messages[1].msg
        self.assertEqual(str(message['subject']),
                         'Welcome to the "Test" mailing list')
예제 #8
0
    def test_send_digest_to_one_missing_and_one_existing_list(self):
        msg = mfs("""\
To: [email protected]
From: [email protected]
Subject: message 1

""")
        self._handler.process(self._mlist, msg, {})
        del msg['subject']
        msg['subject'] = 'message 2'
        self._handler.process(self._mlist, msg, {})
        # There are no digests already being sent, but the ant mailing list
        # does have a digest mbox collecting messages.
        get_queue_messages('digest', expected_count=0)
        mailbox_path = os.path.join(self._mlist.data_path, 'digest.mmdf')
        self.assertGreater(os.path.getsize(mailbox_path), 0)
        args = FakeArgs()
        args.send = True
        args.lists.extend(('ant.example.com', 'bee.example.com'))
        stderr = StringIO()
        with patch('mailman.commands.cli_digests.sys.stderr', stderr):
            self._command.process(args)
        self._runner.run()
        # The warning was printed to stderr.
        self.assertEqual(stderr.getvalue(),
                         'No such list found: bee.example.com\n')
        # But ant's digest was still prepared.
        self.assertFalse(os.path.exists(mailbox_path))
        items = get_queue_messages('virgin', expected_count=1)
        digest_contents = str(items[0].msg)
        self.assertIn('Subject: message 1', digest_contents)
        self.assertIn('Subject: message 2', digest_contents)
예제 #9
0
    def test_hold_chain_crosspost(self):
        mlist2 = create_list('*****@*****.**')
        msg = mfs("""\
From: [email protected]
To: [email protected], [email protected]
Subject: A message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        process_chain(self._mlist, msg, {}, start_chain='hold')
        process_chain(mlist2, msg, {}, start_chain='hold')
        # There are four items in the virgin queue.  Two of them are for the
        # list owners who need to moderate the held message, and the other is
        # for anne telling her that her message was held for approval.
        items = get_queue_messages('virgin', expected_count=4)
        anne_froms = set()
        owner_tos = set()
        for item in items:
            if item.msg['to'] == '*****@*****.**':
                anne_froms.add(item.msg['from'])
            else:
                owner_tos.add(item.msg['to'])
        self.assertEqual(anne_froms, set(['*****@*****.**',
                                          '*****@*****.**']))
        self.assertEqual(owner_tos, set(['*****@*****.**',
                                         '*****@*****.**']))
        # And the message appears in the store.
        messages = list(getUtility(IMessageStore).messages)
        self.assertEqual(len(messages), 1)
        self.assertEqual(messages[0]['message-id'], '<ant>')
예제 #10
0
    def test_hold_chain(self):
        msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: A message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        msgdata = dict(moderation_reasons=[
            'TEST-REASON-1',
            'TEST-REASON-2',
            ])
        logfile = LogFileMark('mailman.vette')
        process_chain(self._mlist, msg, msgdata, start_chain='hold')
        messages = get_queue_messages('virgin', expected_count=2)
        payloads = {}
        for item in messages:
            if item.msg['to'] == '*****@*****.**':
                part = item.msg.get_payload(0)
                payloads['owner'] = part.get_payload().splitlines()
            elif item.msg['To'] == '*****@*****.**':
                payloads['sender'] = item.msg.get_payload().splitlines()
            else:
                self.fail('Unexpected message: %s' % item.msg)
        self.assertIn('    TEST-REASON-1', payloads['owner'])
        self.assertIn('    TEST-REASON-2', payloads['owner'])
        self.assertIn('    TEST-REASON-1', payloads['sender'])
        self.assertIn('    TEST-REASON-2', payloads['sender'])
        logged = logfile.read()
        self.assertIn('TEST-REASON-1', logged)
        self.assertIn('TEST-REASON-2', logged)
예제 #11
0
    def test_priority_site_over_list(self):
        # Test that the site-wide checks take precedence over the list-specific
        # checks.
        msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: A message
Message-ID: <ant>
Foo: foo
MIME-Version: 1.0

A message body.
""")
        msgdata = {}
        header_matches = IHeaderMatchList(self._mlist)
        header_matches.append('Foo', 'foo', 'accept')
        # This event subscriber records the event that occurs when the message
        # is processed by the owner chain.
        events = []
        with event_subscribers(events.append):
            process(self._mlist, msg, msgdata, start_chain='header-match')
        self.assertEqual(len(events), 1)
        event = events[0]
        # Site-wide wants to hold the message, the list wants to accept it.
        self.assertIsInstance(event, HoldEvent)
        self.assertEqual(event.chain, config.chains['hold'])
예제 #12
0
    def test_banned_address_linked_to_user(self):
        # Anne is subscribed to a mailing list as a user with her preferred
        # address.  She also has a secondary address which is banned and which
        # she uses to post to the mailing list.  Both the MemberModeration and
        # NonmemberModeration rules miss because the posting address is
        # banned.
        user_manager = getUtility(IUserManager)
        anne = user_manager.create_user('*****@*****.**')
        set_preferred(anne)
        self._mlist.subscribe(anne, MemberRole.member)
        anne.link(user_manager.create_address('*****@*****.**'))
        IBanManager(self._mlist).ban('*****@*****.**')
        msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        rule = moderation.MemberModeration()
        result = rule.check(self._mlist, msg, {})
        self.assertFalse(result)
        rule = moderation.NonmemberModeration()
        result = rule.check(self._mlist, msg, {})
        self.assertFalse(result)
예제 #13
0
    def test_member_and_nonmember(self):
        user_manager = getUtility(IUserManager)
        anne = user_manager.create_address('*****@*****.**')
        user_manager.create_address('*****@*****.**')
        self._mlist.subscribe(anne, MemberRole.member)
        rule = moderation.NonmemberModeration()
        msg = mfs("""\
From: [email protected]
Sender: [email protected]
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        # Both Anne and Bill are in the message's senders list.
        self.assertIn('*****@*****.**', msg.senders)
        self.assertIn('*****@*****.**', msg.senders)
        # The NonmemberModeration rule should *not* hit, because even though
        # Bill is in the list of senders he is not a member of the mailing
        # list.  Anne is also in the list of senders and she *is* a member, so
        # she takes precedence.
        result = rule.check(self._mlist, msg, {})
        self.assertFalse(result, 'NonmemberModeration rule should not hit')
        # After the rule runs, Bill becomes a non-member.
        bill_member = self._mlist.nonmembers.get_member('*****@*****.**')
        self.assertIsNotNone(bill_member)
        # Bill is not a member.
        bill_member = self._mlist.members.get_member('*****@*****.**')
        self.assertIsNone(bill_member)
예제 #14
0
    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        self._member = subscribe(self._mlist, 'Anne', email='*****@*****.**')
        self._msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: You bounced
Message-ID: <first>

""")
        # Set up the translation context.
        self._var_dir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, self._var_dir)
        xx_template_path = os.path.join(
            self._var_dir, 'templates', 'site', 'xx', 'probe.txt')
        os.makedirs(os.path.dirname(xx_template_path))
        config.push('xx template dir', """\
        [paths.testing]
        var_dir: {}
        """.format(self._var_dir))
        self.addCleanup(config.pop, 'xx template dir')
        language_manager = getUtility(ILanguageManager)
        language_manager.add('xx', 'utf-8', 'Freedonia')
        self._member.preferences.preferred_language = 'xx'
        with open(xx_template_path, 'w') as fp:
            print("""\
blah blah blah
$listname
$address
$optionsurl
$owneraddr
""", file=fp)
예제 #15
0
    def test_bump_before_send(self):
        self._mlist.digest_volume_frequency = DigestFrequency.monthly
        self._mlist.volume = 7
        self._mlist.next_digest_number = 4
        self._mlist.digest_last_sent_at = right_now() + timedelta(
            days=-32)
        msg = mfs("""\
To: [email protected]
From: [email protected]
Subject: message 1

""")
        self._handler.process(self._mlist, msg, {})
        args = FakeArgs()
        args.bump = True
        args.send = True
        args.lists.append('ant.example.com')
        self._command.process(args)
        self._runner.run()
        # The volume is 8 and the digest number is 2 because a digest was sent
        # after the volume/number was bumped.
        self.assertEqual(self._mlist.volume, 8)
        self.assertEqual(self._mlist.next_digest_number, 2)
        self.assertEqual(self._mlist.digest_last_sent_at, right_now())
        items = get_queue_messages('virgin', expected_count=1)
        self.assertEqual(items[0].msg['subject'], 'Ant Digest, Vol 8, Issue 1')
예제 #16
0
    def test_send_one_digest_by_fqdn_listname(self):
        msg = mfs("""\
To: [email protected]
From: [email protected]
Subject: message 1

""")
        self._handler.process(self._mlist, msg, {})
        del msg['subject']
        msg['subject'] = 'message 2'
        self._handler.process(self._mlist, msg, {})
        # There are no digests already being sent, but the ant mailing list
        # does have a digest mbox collecting messages.
        get_queue_messages('digest', expected_count=0)
        mailbox_path = os.path.join(self._mlist.data_path, 'digest.mmdf')
        self.assertGreater(os.path.getsize(mailbox_path), 0)
        args = FakeArgs()
        args.send = True
        args.lists.append('*****@*****.**')
        self._command.process(args)
        self._runner.run()
        # Now, there's no digest mbox and there's a plaintext digest in the
        # outgoing queue.
        self.assertFalse(os.path.exists(mailbox_path))
        items = get_queue_messages('virgin', expected_count=1)
        digest_contents = str(items[0].msg)
        self.assertIn('Subject: message 1', digest_contents)
        self.assertIn('Subject: message 2', digest_contents)
예제 #17
0
 def test_request_order(self):
     # Requests must be sorted in creation order.
     #
     # This test only "works" for PostgreSQL, in the sense that if you
     # remove the fix in ../requests.py, it will still pass in SQLite.
     # Apparently SQLite auto-sorts results by ID but PostgreSQL autosorts
     # by insertion time.  It's still worth keeping the test to prevent
     # regressions.
     #
     # We modify the auto-incremented ids by listening to SQLAlchemy's
     # flush event, and hacking all the _Request object id's to the next
     # value in a descending counter.
     request_ids = []
     counter = count(200, -1)
     def id_hacker(session, flush_context, instances):   # noqa
         for instance in session.new:
             if isinstance(instance, _Request):
                 instance.id = next(counter)
     with before_flush(id_hacker):
         for index in range(10):
             msg = mfs(self._msg.as_string())
             msg.replace_header('Message-ID', '<alpha{}>'.format(index))
             request_ids.append(hold_message(self._mlist, msg))
         config.db.store.flush()
     # Make sure that our ID are not already sorted.
     self.assertNotEqual(request_ids, sorted(request_ids))
     # Get requests and check their order.
     requests = self._requests_db.of_type(RequestType.held_message)
     self.assertEqual([r.id for r in requests], sorted(request_ids))
예제 #18
0
    def test_moderation_reason(self):
        # When a message is moderated, a reason is added to the metadata.
        user_manager = getUtility(IUserManager)
        anne = user_manager.create_address('*****@*****.**')
        msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        # Anne is in the message's senders list.
        self.assertIn('*****@*****.**', msg.senders)
        # Now run the rule.
        rule = moderation.NonmemberModeration()
        msgdata = {}
        result = rule.check(self._mlist, msg, msgdata)
        self.assertTrue(result, 'NonmemberModeration rule should hit')
        # The reason for moderation should be in the msgdata.
        reasons = msgdata['moderation_reasons']
        self.assertEqual(reasons, ['The message is not from a list member'])
        # Now make Anne a moderated member...
        anne_member = self._mlist.subscribe(anne, MemberRole.member)
        anne_member.moderation_action = Action.hold
        # ...and run the rule again.
        rule = moderation.MemberModeration()
        msgdata = {}
        result = rule.check(self._mlist, msg, msgdata)
        self.assertTrue(result, 'MemberModeration rule should hit')
        # The reason for moderation should be in the msgdata.
        reasons = msgdata['moderation_reasons']
        self.assertEqual(
            reasons, ['The message comes from a moderated member'])
예제 #19
0
    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        self._now = now()
        # Enable just the dummy archiver.
        config.push('dummy', """
        [archiver.dummy]
        class: mailman.runners.tests.test_archiver.DummyArchiver
        enable: no
        [archiver.prototype]
        enable: no
        [archiver.mhonarc]
        enable: no
        [archiver.mail_archive]
        enable: no
        """)
        self._archiveq = config.switchboards['archive']
        self._msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: My first post
Message-ID: <first>
X-Message-ID-Hash: 4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB

First post!
""")
        self._runner = make_testable_runner(ArchiveRunner)
        IListArchiverSet(self._mlist).get('dummy').is_enabled = True
예제 #20
0
    def test_crash_event(self):
        runner = make_testable_runner(CrashingRunner, 'in')
        # When an exception occurs in Runner._process_one_file(), a zope.event
        # gets triggered containing the exception object.
        msg = mfs("""\
From: [email protected]
To: [email protected]
Message-ID: <ant>

""")
        config.switchboards['in'].enqueue(msg, listid='test.example.com')
        with event_subscribers(self._got_event):
            runner.run()
        # We should now have exactly one event, which will contain the
        # exception, plus additional metadata containing the mailing list,
        # message, and metadata.
        self.assertEqual(len(self._events), 1)
        event = self._events[0]
        self.assertTrue(isinstance(event, RunnerCrashEvent))
        self.assertEqual(event.mailing_list, self._mlist)
        self.assertEqual(event.message['message-id'], '<ant>')
        self.assertEqual(event.metadata['listid'], 'test.example.com')
        self.assertTrue(isinstance(event.error, RuntimeError))
        self.assertEqual(str(event.error), 'borked')
        self.assertTrue(isinstance(event.runner, CrashingRunner))
        # The message should also have ended up in the shunt queue.
        shunted = get_queue_messages('shunt')
        self.assertEqual(len(shunted), 1)
        self.assertEqual(shunted[0].msg['message-id'], '<ant>')
예제 #21
0
    def setUp(self):
        # Create a fake mailing list and message object
        self._msg = mfs("""\
To: [email protected]
From: [email protected]
Subject: Testing the test list
Message-ID: <ant>
X-Message-ID-Hash: MS6QLWERIJLGCRF44J7USBFDELMNT2BW

Tests are better than no tests
but the water deserves to be swum.
""")
        with transaction():
            self._mlist = create_list('*****@*****.**')
        # Set up a temporary directory for the prototype archiver so that it's
        # easier to clean up.
        self._tempdir = tempfile.mkdtemp()
        config.push('prototype', """
        [paths.testing]
        archive_dir: {0}
        """.format(self._tempdir))
        # Capture the structure of a maildir.
        self._expected_dir_structure = set(
            (os.path.join(config.ARCHIVE_DIR, path) for path in (
                'prototype',
                os.path.join('prototype', self._mlist.fqdn_listname),
                os.path.join('prototype', self._mlist.fqdn_listname, 'cur'),
                os.path.join('prototype', self._mlist.fqdn_listname, 'new'),
                os.path.join('prototype', self._mlist.fqdn_listname, 'tmp'),
                )))
        self._expected_dir_structure.add(config.ARCHIVE_DIR)
예제 #22
0
    def test_member_fallback_to_list_defaults(self):
        # https://gitlab.com/mailman/mailman/issues/189
        self._mlist.default_member_action = Action.accept
        user_manager = getUtility(IUserManager)
        anne = user_manager.create_address('*****@*****.**')
        member = self._mlist.subscribe(anne, MemberRole.member)
        # Anne's moderation rule falls back to the list default.
        self.assertIsNone(member.moderation_action)
        rule = moderation.MemberModeration()
        msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        # First, the message gets accepted.
        msgdata = {}
        result = rule.check(self._mlist, msg, msgdata)
        self.assertTrue(result)
        self.assertEqual(msgdata.get('moderation_action'), 'accept')
        # Then the list's default member action is changed.
        self._mlist.default_member_action = Action.hold
        msg.replace_header('Message-ID', '<bee>')
        # This time, the message is held.
        result = rule.check(self._mlist, msg, msgdata)
        self.assertTrue(result)
        self.assertEqual(msgdata.get('moderation_action'), 'hold')
예제 #23
0
    def test_nonmember_fallback_to_list_defaults(self):
        # https://gitlab.com/mailman/mailman/issues/189
        self._mlist.default_nonmember_action = Action.hold
        user_manager = getUtility(IUserManager)
        user_manager.create_address('*****@*****.**')
        rule = moderation.NonmemberModeration()
        msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        # First, the message should get held.
        msgdata = {}
        result = rule.check(self._mlist, msg, msgdata)
        self.assertTrue(result)
        self.assertEqual(msgdata['moderation_action'], 'hold')
        # As a side-effect, Anne has been added as a nonmember with a
        # moderation action that falls back to the list's default.
        anne = self._mlist.nonmembers.get_member('*****@*****.**')
        self.assertIsNone(anne.moderation_action)
        # Then the list's default nonmember action is changed.
        self._mlist.default_nonmember_action = Action.discard
        msg.replace_header('Message-ID', '<bee>')
        # This time, the message should be discarded.
        result = rule.check(self._mlist, msg, msgdata)
        self.assertTrue(result)
        self.assertEqual(msgdata.get('moderation_action'), 'discard')
예제 #24
0
    def test_simple_message(self):
        msg = mfs("""\
From: [email protected]
To: [email protected]

message triggering a digest
""")
        mbox_path = os.path.join(self._mlist.data_path, 'digest.mmdf')
        self._process(self._mlist, msg, {})
        self._digestq.enqueue(
            msg,
            listname=self._mlist.fqdn_listname,
            digest_path=mbox_path,
            volume=1, digest_number=1)
        self._runner.run()
        # There are two messages in the virgin queue: the digest as plain-text
        # and as multipart.
        messages = get_queue_messages('virgin')
        self.assertEqual(len(messages), 2)
        self.assertEqual(
            sorted(item.msg.get_content_type() for item in messages),
            ['multipart/mixed', 'text/plain'])
        for item in messages:
            self.assertEqual(item.msg['subject'],
                             'Test Digest, Vol 1, Issue 1')
예제 #25
0
    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 {0}'.format(self._token)
        to = 'test-confirm+{0}@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
>     [email protected]
>=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
>     [email protected]

--=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('*****@*****.**')
        address = list(user.addresses)[0]
        self.assertEqual(address.email, '*****@*****.**')
        self.assertEqual(address.verified_on, datetime(2005, 8, 1, 7, 49, 23))
        address = manager.get_address('*****@*****.**')
        self.assertEqual(address.email, '*****@*****.**')
        messages = get_queue_messages('virgin')
        self.assertEqual(len(messages), 1)
        self.assertEqual(messages[0].msgdata['recipients'],
                         set(['*****@*****.**']))
예제 #26
0
    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        self._msg = mfs("""\
From: [email protected]
To: [email protected]
Message-ID: <ant>

""")
예제 #27
0
    def test_no_verp_with_non_match(self):
        # No VERP address is found, and a header had a non-matching pattern.
        msg = mfs("""\
From: [email protected]
To: [email protected]

""")
        self.assertEqual(self._verper.get_verp(self._mlist, msg), set())
예제 #28
0
    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        self._handler = config.handlers['file-recipients'].process
        self._msg = mfs("""\
From: [email protected]

A message.
""")
예제 #29
0
    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        self._msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: Ignore

""")
예제 #30
0
    def test_no_verp(self):
        # The empty set is returned when there is no VERP headers.
        msg = mfs("""\
From: [email protected]
To: [email protected]

""")
        self.assertEqual(self._verper.get_verp(self._mlist, msg), set())
예제 #31
0
    def test_get_message_by_hash(self):
        # Messages have an X-Message-ID-Hash header, the value of which can be
        # used to look the message up in the message store.
        message = mfs("""\
Subject: An important message
Message-ID: <ant>

This message is very important.
""")
        add_message_hash(message)
        self._store.add(message)
        self.assertEqual(message['x-message-id-hash'],
                         'V3YEHAFKE2WVJNK63Z7RFP4JMHISI2RG')
        found = self._store.get_message_by_hash(
            'V3YEHAFKE2WVJNK63Z7RFP4JMHISI2RG')
        self.assertEqual(found['message-id'], '<ant>')
        self.assertEqual(found['x-message-id-hash'],
                         'V3YEHAFKE2WVJNK63Z7RFP4JMHISI2RG')
예제 #32
0
    def test_no_bak_but_pck(self):
        # if there is no .bak file but a .pck with the same filebase,
        # .finish() should handle the .pck.
        msg = mfs("""\
From: [email protected]
To: [email protected]
Message-ID: <ant>

""")
        switchboard = config.switchboards['shunt']
        # Enqueue the message.
        filebase = switchboard.enqueue(msg)
        # Now call .finish() without first dequeueing.
        switchboard.finish(filebase, preserve=True)
        # And ensure the file got preserved.
        bad_dir = config.switchboards['bad'].queue_directory
        psvfile = os.path.join(bad_dir, filebase + '.psv')
        self.assertTrue(os.path.isfile(psvfile))
예제 #33
0
    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        self._manager = getUtility(IUserManager)
        anne = self._manager.create_address('*****@*****.**')
        bart = self._manager.create_address('*****@*****.**')
        cris = self._manager.create_address('*****@*****.**')
        dave = self._manager.create_address('*****@*****.**')
        # Make Cris and Dave owners of the mailing list.
        self._anne = self._mlist.subscribe(anne, MemberRole.member)
        self._bart = self._mlist.subscribe(bart, MemberRole.member)
        self._cris = self._mlist.subscribe(cris, MemberRole.owner)
        self._dave = self._mlist.subscribe(dave, MemberRole.owner)
        self._process = config.handlers['owner-recipients'].process
        self._msg = mfs("""\
From: Elle Person <*****@*****.**>
To: [email protected]

""")
예제 #34
0
    def test_max_recipients_returns_reason(self):
        # Ensure max_recipients rule returns a reason.
        msg = mfs("""\
From: [email protected]
To: [email protected]
Cc: [email protected], [email protected]
Subject: A Subject
Message-ID: <ant>

A message body.
""")
        rule = max_recipients.MaximumRecipients()
        self._mlist.max_num_recipients = 2
        msgdata = {}
        result = rule.check(self._mlist, msg, msgdata)
        self.assertTrue(result)
        self.assertEqual(msgdata['moderation_reasons'],
                         [('Message has more than {} recipients', 2)])
예제 #35
0
    def test_message_id_hash_gets_replaced_backward_compatibility(self):
        msg = mfs("""\
Message-ID: <ant>
Message-ID-Hash: abc
X-Message-ID-Hash: abc

""")
        self._store.add(msg)
        stored_msg = self._store.get_message_by_id('<ant>')
        message_id_hashes = stored_msg.get_all('message-id-hash')
        self.assertEqual(len(message_id_hashes), 1)
        self.assertEqual(message_id_hashes[0],
                         'MS6QLWERIJLGCRF44J7USBFDELMNT2BW')
        # For backward compatibility with the old spec.
        x_message_id_hashes = stored_msg.get_all('x-message-id-hash')
        self.assertEqual(len(x_message_id_hashes), 1)
        self.assertEqual(x_message_id_hashes[0],
                         'MS6QLWERIJLGCRF44J7USBFDELMNT2BW')
예제 #36
0
    def test_subject_encoding_error(self):
        # GL#383: messages with badly encoded Subject headers crash the REST
        # server.
        self._msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: =?GB2312?B?saa9o7fmtNPEpbVaQ2h1o6zDt7uoz+PX1L/guq7AtKGj?=
Message-ID: <alpha>

Something else.
""")
        with transaction():
            held_id = hold_message(self._mlist, self._msg)
        json, response = call_api(
            'http://*****:*****@example.com/held')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(json['total_size'], 1)
        self.assertEqual(json['entries'][0]['request_id'], held_id)
    def test_confirm_leave_moderate(self):
        msg = mfs("""\
From: Anne Person <*****@*****.**>
To: test-confirm+{token}@example.com
Subject: Re: confirm {token}

""".format(token=self._token))
        self._mlist.unsubscription_policy = (
            SubscriptionPolicy.confirm_then_moderate)
        # Clear any previously queued confirmation messages.
        get_queue_messages('virgin')
        Confirm().process(self._mlist, msg, {}, (self._token, ), Results())
        # Anne is still a member of the mailing list.
        member = self._mlist.members.get_member('*****@*****.**')
        self.assertIsNotNone(member)
        # There should be a notice to the list owners
        item = get_queue_messages('virgin', expected_count=1)[0]
        self.assertEqual(item.msg['to'], '*****@*****.**')
예제 #38
0
    def test_hold_chain(self):
        msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: A message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        msgdata = dict(moderation_reasons=[
            'TEST-REASON-1',
            'TEST-REASON-2',
            ('TEST-{}-REASON-{}', 'FORMAT', 3),
        ])
        logfile = LogFileMark('mailman.vette')
        process_chain(self._mlist, msg, msgdata, start_chain='hold')
        messages = get_queue_messages('virgin', expected_count=2)
        payloads = {}
        for item in messages:
            if item.msg['to'] == '*****@*****.**':
                part = item.msg.get_payload(0)
                payloads['owner'] = part.get_payload().splitlines()
            elif item.msg['To'] == '*****@*****.**':
                payloads['sender'] = item.msg.get_payload().splitlines()
            else:
                self.fail('Unexpected message: %s' % item.msg)
        self.assertIn('    TEST-REASON-1', payloads['owner'])
        self.assertIn('    TEST-REASON-2', payloads['owner'])
        self.assertIn('    TEST-FORMAT-REASON-3', payloads['owner'])
        self.assertIn('    TEST-REASON-1', payloads['sender'])
        self.assertIn('    TEST-REASON-2', payloads['sender'])
        self.assertIn('    TEST-FORMAT-REASON-3', payloads['sender'])
        logged = logfile.read()
        self.assertIn('TEST-REASON-1', logged)
        self.assertIn('TEST-REASON-2', logged)
        self.assertIn('TEST-FORMAT-REASON-3', logged)
        # Check the reason passed to hold_message().
        requests = IListRequests(self._mlist)
        self.assertEqual(requests.count_of(RequestType.held_message), 1)
        request = requests.of_type(RequestType.held_message)[0]
        key, data = requests.get_request(request.id)
        self.assertEqual(data.get('_mod_reason'),
                         'TEST-REASON-1; TEST-REASON-2; TEST-FORMAT-REASON-3')
    def test_reply_to_list(self):
        # Test a post from a member with the list posting address in Reply-To:.
        rule = moderation.NonmemberModeration()
        user_manager = getUtility(IUserManager)
        anne = user_manager.create_address('*****@*****.**')
        user_manager.create_address('*****@*****.**')
        self._mlist.subscribe(anne, MemberRole.member)
        msg = mfs("""\
From: [email protected]
To: [email protected]
Reply-To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        result = rule.check(self._mlist, msg, {})
        self.assertFalse(result)
예제 #40
0
    def test_confirm_with_random_ascii_prefix(self):
        subject = '\x99AW: confirm {}'.format(self._token)
        msg = mfs("""\
From: [email protected]
To: [email protected]

""")
        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('*****@*****.**')
        address = list(user.addresses)[0]
        self.assertEqual(address.email, '*****@*****.**')
        self.assertEqual(address.verified_on, datetime(2005, 8, 1, 7, 49, 23))
        address = manager.get_address('*****@*****.**')
        self.assertEqual(address.email, '*****@*****.**')
예제 #41
0
    def test_dmarc_dns_exception(self):
        mlist = create_list('*****@*****.**')
        # Use action reject.  The rule only hits on reject and discard.
        mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
        msg = mfs("""\
From: [email protected]
To: [email protected]

""")
        mark = LogFileMark('mailman.error')
        rule = dmarc.DMARCMitigation()
        with get_dns_resolver():
            self.assertTrue(rule.check(mlist, msg, {}))
        line = mark.readline()
        self.assertEqual(
            line[-144:],
            'DNSException: Unable to query DMARC policy for '
            '[email protected] (_dmarc.example.info). '
            'Abstract base class shared by all dnspython exceptions.\n')
예제 #42
0
    def test_confirm_subcommand_with_more_commands(self):
        # confirm command stops the processing of rest of the commands.
        get_queue_messages('virgin')
        subject = 'bad-command'
        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
""")
        msg['Subject'] = subject
        msg['To'] = to
        self._commandq.enqueue(
            msg, dict(listid='test.example.com', subaddress='confirm'))
        self._runner.run()
        # This should not send out any email.
        get_queue_messages('virgin', expected_count=0)
예제 #43
0
    def test_multiple_cnames(self):
        mlist = create_list('*****@*****.**')
        # Use action reject.  The rule only hits on reject and discard.
        mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
        msg = mfs("""\
From: [email protected]
To: [email protected]

""")
        mark = LogFileMark('mailman.vette')
        rule = dmarc.DMARCMitigation()
        with get_dns_resolver(cmult=True):
            self.assertTrue(rule.check(mlist, msg, {}))
        line = mark.readline()
        self.assertEqual(
            line[-128:],
            'ant: DMARC lookup for [email protected] (_dmarc.example.biz) '
            'found p=quarantine in _dmarc.example.com. = v=DMARC1; '
            'p=quarantine;\n')
    def test_these_nonmembers(self):
        # Test the legacy *_these_nonmembers attributes.
        user_manager = getUtility(IUserManager)
        actions = {
            '*****@*****.**': 'accept',
            '*****@*****.**': 'hold',
            '*****@*****.**': 'reject',
            '*****@*****.**': 'discard',
            '^anne-.*@example.com': 'accept',
            '^bill-.*@example.com': 'hold',
            '^chris-.*@example.com': 'reject',
            '^dana-.*@example.com': 'discard',
        }
        rule = moderation.NonmemberModeration()
        user_manager = getUtility(IUserManager)
        for address, action_name in actions.items():
            setattr(self._mlist, '{}_these_nonmembers'.format(action_name),
                    [address])
            if address.startswith('^'):
                # It's a pattern, craft a proper address.
                address = address[1:].replace('.*', 'something')
            user_manager.create_address(address)
            msg = mfs("""\
From: {}
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""".format(address))
            msgdata = {}
            result = rule.check(self._mlist, msg, msgdata)
            if action_name == 'accept':
                self.assertFalse(result,
                                 'NonmemberModeration rule should miss')
            else:
                self.assertTrue(result, 'NonmemberModeration rule should hit')
                self.assertIn('member_moderation_action', msgdata)
                self.assertEqual(
                    msgdata['member_moderation_action'], action_name,
                    'Wrong action for {}: {}'.format(address, action_name))
예제 #45
0
파일: test_join.py 프로젝트: inonit/mailman
    def test_double_confirmation(self):
        # A join request comes in using both the -join address and the word
        # 'subscribe' in the first line of the body.  This should produce just
        # one subscription request and one confirmation response.
        msg = mfs("""\
From: [email protected]
To: [email protected]

subscribe
""")
        # Adding the subaddress to the metadata dictionary mimics what happens
        # when the above email message is first processed by the lmtp runner.
        # For convenience, we skip that step in this test.
        self._commandq.enqueue(
            msg, dict(listid='test.example.com', subaddress='join'))
        self._runner.run()
        # There will be two messages in the queue.  The first one is a reply
        # to Anne notifying her of the status of her command email.  The
        # second one is the confirmation message of her join request.
        items = get_queue_messages('virgin',
                                   sort_on='subject',
                                   expected_count=2)
        self.assertTrue(str(items[1].msg['subject']).startswith('confirm'))
        self.assertEqual(items[0].msg['subject'],
                         'The results of your email commands')
        # 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
        # There should be exactly one confirmation line.
        self.assertEqual(len(confirmation_lines), 1)
        # And the confirmation line should name Anne's email address.
        self.assertIn('*****@*****.**', confirmation_lines[0])
예제 #46
0
    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        # Set personalized delivery.
        self._mlist.personalize = Personalization.individual
        # Make Anne a member of this mailing list.
        self._anne = subscribe(self._mlist, 'Anne', email='*****@*****.**')
        self._msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: test

""")
        self._deliverer = find_name(config.mta.outgoing)
        # Set the maximum transactions per connection.
        config.push(
            'maxtrans', """
        [mta]
        max_sessions_per_connection: 3
        """)
        self.addCleanup(config.pop, 'maxtrans')
예제 #47
0
    def setUp(self):
        self._mlist = create_list('*****@*****.**')
        # The default testing hash algorithm is "roundup_plaintext" which
        # yields hashed passwords of the form: {plaintext}abc
        #
        # Migration is automatically supported when a more modern password
        # hash is chosen after the original password is set.  As long as the
        # old password still validates, the migration happens automatically.
        self._mlist.moderator_password = config.password_context.encrypt(
            'super secret')
        self._rule = approved.Approved()
        self._msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: A Message with non-ascii body
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
예제 #48
0
    def test_banned_sender_among_multiple_senders(self):
        # Two addresses are created, one of which is banned.  The rule matches
        # because all senders are checked.
        user_manager = getUtility(IUserManager)
        user_manager.create_address('*****@*****.**')
        user_manager.create_address('*****@*****.**')
        IBanManager(self._mlist).ban('*****@*****.**')
        msg = mfs("""\
From: [email protected]
Sender: [email protected]
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        rule = banned_address.BannedAddress()
        result = rule.check(self._mlist, msg, {})
        self.assertTrue(result)
예제 #49
0
    def test_missing_html_to_plain_text_command(self):
        # Calling a missing html_to_plain_text_command is properly logged.
        msg = mfs("""\
From: [email protected]
Content-Type: text/html
MIME-Version: 1.0

<html><head></head>
<body></body></html>
""")
        process = config.handlers['mime-delete'].process
        mark = LogFileMark('mailman.error')
        with dummy_script('nonexist'):
            process(self._mlist, msg, {})
        line = mark.readline()[:-1]
        self.assertTrue(line.endswith('HTML -> text/plain command error'))
        self.assertEqual(msg.get_content_type(), 'text/html')
        self.assertIsNone(msg['x-content-filtered-by'])
        payload_lines = msg.get_payload().splitlines()
        self.assertEqual(payload_lines[0], '<html><head></head>')
예제 #50
0
    def test_max_size_returns_reason(self):
        # Ensure max_size rule returns a reason.
        msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: A Subject
Message-ID: <ant>

A message body.
""")
        rule = max_size.MaximumSize()
        self._mlist.max_message_size = 1
        # Fake the size.
        msg.original_size = 2048
        msgdata = {}
        result = rule.check(self._mlist, msg, msgdata)
        self.assertTrue(result)
        self.assertEqual(
            msgdata['moderation_reasons'],
            [('The message is larger than the {} KB maximum size', 1)])
예제 #51
0
    def test_leave(self):
        with transaction():
            self._mlist.unsubscription_policy = SubscriptionPolicy.confirm
            anne = getUtility(IUserManager).create_user('*****@*****.**')
            set_preferred(anne)
            self._mlist.subscribe(anne.preferred_address)
        msg = mfs("""\
From: [email protected]
To: [email protected]

leave
""")
        self._commandq.enqueue(
            msg, dict(listid='test.example.com', subaddress='leave'))
        self._runner.run()
        # One message with confirmation of her unsubscription event should be
        # sent.
        items = get_queue_messages('virgin', expected_count=1)
        confirmation = items[0].msg
        self.assertTrue(str(confirmation['subject']).startswith('confirm'))
예제 #52
0
    def test_double_leave(self):
        # In this case, the user can be unsubscribed immediately because the
        # policy does not require confirmation, however because the email is
        # sent to the -leave address and it contains the 'leave' command, we
        # should only process one command per email.
        with transaction():
            self._mlist.unsubscription_policy = SubscriptionPolicy.open
            anne = getUtility(IUserManager).create_user('*****@*****.**')
            set_preferred(anne)
            self._mlist.subscribe(anne.preferred_address)
        msg = mfs("""\
From: [email protected]
To: [email protected]

leave
""")
        self._commandq.enqueue(
            msg, dict(listid='test.example.com', subaddress='leave'))
        self._runner.run()
        get_queue_messages('virgin', sort_on='subject', expected_count=0)
예제 #53
0
    def test_convert_html_to_plaintext_base64(self):
        # Converting to plain text calls a command line script with decoded
        # message body.
        msg = mfs("""\
From: [email protected]
Content-Type: text/html
Content-Transfer-Encoding: base64
MIME-Version: 1.0

PGh0bWw+PGhlYWQ+PC9oZWFkPgo8Ym9keT48L2JvZHk+PC9odG1sPgo=
""")
        process = config.handlers['mime-delete'].process
        with dummy_script():
            process(self._mlist, msg, {})
        self.assertEqual(msg.get_content_type(), 'text/plain')
        self.assertTrue(
            msg['x-content-filtered-by'].startswith('Mailman/MimeDel'))
        payload_lines = msg.get_payload().splitlines()
        self.assertEqual(payload_lines[0], 'Converted text/html to text/plain')
        self.assertEqual(payload_lines[2], '<html><head></head>')
예제 #54
0
    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 {0}'.format(self._token)
        to = 'test-confirm+{0}@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('*****@*****.**')
        self.assertEqual(list(user.addresses)[0].email, '*****@*****.**')
        # Make sure that the confirmation was not attempted twice.
        messages = get_queue_messages('virgin')
        self.assertEqual(len(messages), 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(messages[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.assertFalse('did not match' in confirmation_lines[0])
예제 #55
0
    def test_no_text_plain_part(self):
        # When the message body only contains HTML, the rule should not throw
        # AttributeError: 'NoneType' object has no attribute 'get_payload'
        # LP: #1158721
        msg = mfs("""\
From: [email protected]
To: [email protected]
Subject: HTML only email
Message-ID: <ant>
MIME-Version: 1.0
Content-Type: text/html; charset="Windows-1251"
Content-Transfer-Encoding: 7bit

<HTML>
<BODY>
<P>This message contains only HTML, no plain/text part</P>
</BODY>
</HTML>
""")
        result = self._rule.check(self._mlist, msg, {})
        self.assertFalse(result)
예제 #56
0
    def test_banned_sender_among_multiple_senders(self):
        # Two addresses are created, one of which is banned.  Even though the
        # The Nonmember moderation rule misses if any of the banned addresses
        # appear in the 'senders' headers of the message.
        user_manager = getUtility(IUserManager)
        user_manager.create_address('*****@*****.**')
        user_manager.create_address('*****@*****.**')
        IBanManager(self._mlist).ban('*****@*****.**')
        rule = moderation.NonmemberModeration()
        msg = mfs("""\
From: [email protected]
Sender: [email protected]
To: [email protected]
Subject: A test message
Message-ID: <ant>
MIME-Version: 1.0

A message body.
""")
        result = rule.check(self._mlist, msg, {})
        self.assertFalse(result)
예제 #57
0
    def setUp(self):
        config.push('no_archivers', """
        [archiver.prototype]
        enable: no
        [archiver.mail_archive]
        enable: no
        [archiver.mhonarc]
        enable: no
        [archiver.pipermail]
        enable: no
        """)
        self.addCleanup(config.pop, 'no_archivers')
        self._mlist = create_list('*****@*****.**')
        self._mlist.archive_policy = ArchivePolicy.public
        self._msg = mfs("""\
From: [email protected]
Message-ID: <first>
Message-ID-Hash: 4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB

Dummy text
""")
예제 #58
0
    def test_log_exception_in_finish(self):
        # If something bad happens in .finish(), the traceback should get
        # logged.  LP: #1165589.
        msg = mfs("""\
From: [email protected]
To: [email protected]
Message-ID: <ant>

""")
        switchboard = config.switchboards['shunt']
        # Enqueue the message.
        filebase = switchboard.enqueue(msg)
        error_log = LogFileMark('mailman.error')
        msg, data = switchboard.dequeue(filebase)
        # Now, cause .finish() to throw an exception.
        with patch('mailman.core.switchboard.os.rename',
                   side_effect=OSError('Oops!')):
            switchboard.finish(filebase, preserve=True)
        traceback = error_log.read().splitlines()
        self.assertEqual(traceback[1], 'Traceback (most recent call last):')
        self.assertEqual(traceback[-1], 'OSError: Oops!')
예제 #59
0
    def test_confirm_in_subject_with_more_commands(self):
        get_queue_messages('virgin')
        subject = 'confirm {}'.format(self._token)
        to = '*****@*****.**'
        msg = mfs("""\
From: Anne Person <*****@*****.**>
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

bad-command
""")
        msg['Subject'] = subject
        msg['To'] = to
        self._commandq.enqueue(
            msg, dict(listid='test.example.com', subaddress='confirm'))
        self._runner.run()
        # This should send out one email that confirms that token was accepted.
        items = get_queue_messages('virgin', expected_count=1)
        self.assertNotIn('No such command: bad-command', str(items[0].msg))
예제 #60
0
    def test_message_id_hash_gets_replaced(self):
        # Any existing Message-ID-Hash header (or for backward compatibility
        # X-Message-ID-Hash) gets replaced with its new value.
        msg = mfs("""\
Subject: Testing
Message-ID: <ant>
Message-ID-Hash: abc
X-Message-ID-Hash: abc

""")
        self._store.add(msg)
        stored_msg = self._store.get_message_by_id('<ant>')
        message_id_hashes = stored_msg.get_all('message-id-hash')
        self.assertEqual(len(message_id_hashes), 1)
        self.assertEqual(message_id_hashes[0],
                         'MS6QLWERIJLGCRF44J7USBFDELMNT2BW')
        # For backward compatibility with the old spec.
        x_message_id_hashes = stored_msg.get_all('x-message-id-hash')
        self.assertEqual(len(x_message_id_hashes), 1)
        self.assertEqual(x_message_id_hashes[0],
                         'MS6QLWERIJLGCRF44J7USBFDELMNT2BW')