def test_invariant_encode_decode(self):
     """
     Tests that decoding an encoded address returns the original pair.
     """
     from_email, to_email = '*****@*****.**', '*****@*****.**'
     self.assertEqual(
         verp.decode(verp.encode(from_email, to_email)),
         (from_email, to_email))
    def test_decode(self):
        """
        Tests the decode method.
        """
        self.assertEqual(
            verp.decode('[email protected]'),
            ('*****@*****.**', '*****@*****.**'))

        self.assertEqual(
            verp.decode('[email protected]'),
            ('*****@*****.**', '[email protected]'))

        self.assertEqual(
            verp.decode('[email protected]'),
            ('*****@*****.**', '*****@*****.**'))

        self.assertEqual(
            verp.decode('[email protected]'),
            ('*****@*****.**', 'user+!%-:@[][email protected]'))
    def test_envelope_from_address(self):
        """
        Tests that the envelope from address is created specially for each user
        in order to track their bounced messages.
        """
        self.subscribe_user_to_package('*****@*****.**', self.package_name)

        self.run_dispatch()

        msg = mail.outbox[0]
        bounce_address, user_address = verp.decode(msg.from_email)
        self.assertTrue(bounce_address.startswith('bounces+'))
        self.assertEqual(user_address, msg.to[0])
def handle_bounces(sent_to_address):
    """
    Handles a received bounce message.

    :param sent_to_address: The envelope-to (return path) address to which the
        bounced email was returned.
    :type sent_to_address: string
    """
    bounce_email, user_email = verp.decode(sent_to_address)
    match = re.match(r"^bounces\+(\d{8})@" + PTS_FQDN, bounce_email)
    if not match:
        # Invalid bounce address
        logger.error("Invalid bounce address " + bounce_email)
        return
    try:
        date = datetime.strptime(match.group(1), "%Y%m%d")
    except ValueError:
        # Invalid bounce address
        logger.error("Invalid bounce address " + bounce_email)
        return
    EmailUserBounceStats.objects.add_bounce_for_user(email=user_email, date=date)

    logger.info("Logged bounce for {email} on {date}".format(email=user_email, date=date))
    user = EmailUserBounceStats.objects.get(user_email__email=user_email)
    if user.has_too_many_bounces():
        logger.info("{email} has too many bounces".format(email=user_email))

        email_body = pts_render_to_string(
            "dispatch/unsubscribed-due-to-bounces-email.txt",
            {"email": user_email, "packages": user.packagename_set.all()},
        )
        EmailMessage(
            subject="All your subscriptions from the PTS have been cancelled",
            from_email=settings.PTS_BOUNCES_LIKELY_SPAM_EMAIL,
            to=[user_email],
            cc=[settings.PTS_CONTACT_EMAIL],
            body=email_body,
            headers={"From": settings.PTS_CONTACT_EMAIL},
        ).send()

        user.unsubscribe_all()