def makeThrottledSender(self):
     sender = self.factory.makePerson(email='*****@*****.**')
     old_message = self.factory.makeSignedMessage(email_address='*****@*****.**')
     authorization = IDirectEmailAuthorization(sender)
     for action in xrange(authorization.message_quota):
         authorization.record(old_message)
     return sender
 def makeThrottledSender(self):
     sender = self.factory.makePerson(email='*****@*****.**')
     old_message = self.factory.makeSignedMessage(email_address='*****@*****.**')
     authorization = IDirectEmailAuthorization(sender)
     for action in xrange(authorization.message_quota):
         authorization.record(old_message)
     return sender
 def test_quota_reached_error(self):
     # An error is raised if the user has reached the daily quota.
     self.factory.makePerson(email='*****@*****.**', name='me')
     user = self.factory.makePerson(email='*****@*****.**', name='him')
     recipients_set = NotificationRecipientSet()
     old_message = self.factory.makeSignedMessage(email_address='*****@*****.**')
     authorization = IDirectEmailAuthorization(user)
     for action in xrange(authorization.message_quota):
         authorization.record(old_message)
     self.assertRaises(QuotaReachedError, send_direct_contact_email,
                       '*****@*****.**', recipients_set, 'subject', 'body')
 def test_quota_reached_error(self):
     # An error is raised if the user has reached the daily quota.
     self.factory.makePerson(email='*****@*****.**', name='me')
     user = self.factory.makePerson(email='*****@*****.**', name='him')
     recipients_set = NotificationRecipientSet()
     old_message = self.factory.makeSignedMessage(email_address='*****@*****.**')
     authorization = IDirectEmailAuthorization(user)
     for action in xrange(authorization.message_quota):
         authorization.record(old_message)
     self.assertRaises(
         QuotaReachedError, send_direct_contact_email,
         '*****@*****.**', recipients_set, 'subject', 'body')
 def test_empty_recipient_set(self):
     # The recipient set can be empty. No messages are sent and the
     # action does not count toward the daily quota.
     self.factory.makePerson(email='*****@*****.**', name='me')
     user = self.factory.makePerson(email='*****@*****.**', name='him')
     recipients_set = NotificationRecipientSet()
     old_message = self.factory.makeSignedMessage(email_address='*****@*****.**')
     authorization = IDirectEmailAuthorization(user)
     for action in xrange(authorization.message_quota - 1):
         authorization.record(old_message)
     pop_notifications()
     send_direct_contact_email(
         '*****@*****.**', recipients_set, 'subject', 'body')
     notifications = pop_notifications()
     self.assertEqual(0, len(notifications))
     self.assertTrue(authorization.is_allowed)
 def test_empty_recipient_set(self):
     # The recipient set can be empty. No messages are sent and the
     # action does not count toward the daily quota.
     self.factory.makePerson(email='*****@*****.**', name='me')
     user = self.factory.makePerson(email='*****@*****.**', name='him')
     recipients_set = NotificationRecipientSet()
     old_message = self.factory.makeSignedMessage(email_address='*****@*****.**')
     authorization = IDirectEmailAuthorization(user)
     for action in xrange(authorization.message_quota - 1):
         authorization.record(old_message)
     pop_notifications()
     send_direct_contact_email('*****@*****.**', recipients_set, 'subject',
                               'body')
     notifications = pop_notifications()
     self.assertEqual(0, len(notifications))
     self.assertTrue(authorization.is_allowed)
Example #7
0
 def test_no_warning_for_UserToUserEmail_recipient(self):
     # A UserToUserEmail.recipient reference does not cause a warning.
     # Since the fix for https://bugs.launchpad.net/launchpad/+bug/246022
     # it's no longer possible to create these without also having a
     # TeamMembership.person reference (which separately inhibits
     # visibility changes), but there are still examples on production.
     sender = self.factory.makePerson()
     self.team.setContactAddress(
         getUtility(IEmailAddressSet).new("*****@*****.**", self.team))
     message = MIMEText("")
     message["From"] = format_address_for_person(sender)
     message["To"] = format_address_for_person(self.team)
     message["Subject"] = ""
     message["Message-ID"] = make_msgid("launchpad")
     message["Date"] = formatdate()
     IDirectEmailAuthorization(sender).record(message)
     transaction.commit()
     self.assertEqual(
         None,
         self.team.visibilityConsistencyWarning(PersonVisibility.PRIVATE))
Example #8
0
def send_direct_contact_email(sender_email, recipients_set, person_or_team,
                              subject, body):
    """Send a direct user-to-user email.

    :param sender_email: The email address of the sender.
    :type sender_email: string
    :param recipients_set: The recipients.
    :type recipients_set: `ContactViaWebNotificationSet`
    :param person_or_team: The party that is the context of the email.
    :type person_or_team: `IPerson`
    :param subject: The Subject header.
    :type subject: unicode
    :param body: The message body.
    :type body: unicode
    :return: The sent message.
    :rtype: `email.message.Message`
    """
    # Craft the email message.  Start by checking whether the subject and
    # message bodies are ASCII or not.
    subject_header = encode(subject)
    try:
        body.encode('us-ascii')
        charset = 'us-ascii'
    except UnicodeEncodeError:
        charset = 'utf-8'
    # Get the sender's real name, encoded as per RFC 2047.
    person_set = getUtility(IPersonSet)
    sender = person_set.getByEmail(sender_email)
    assert sender is not None, 'No person for sender %s' % sender_email
    sender_name = str(encode(sender.displayname))
    # Do a single authorization/quota check for the sender.  We consume one
    # quota credit per contact, not per recipient.
    authorization = IDirectEmailAuthorization(sender)
    if not authorization.is_allowed:
        raise QuotaReachedError(sender.displayname, authorization)
    # Add the footer as a unicode string, then encode the body if necessary.
    # This is not entirely optimal if the body has non-ascii characters in it,
    # since the footer may get garbled in a non-MIME aware mail reader.  Who
    # uses those anyway!?  The only alternative is to attach the footer as a
    # MIME attachment with a us-ascii charset, but that has it's own set of
    # problems (and user complaints).  Email sucks.
    additions = u'\n'.join([
        u'',
        u'-- ',
        u'This message was sent from Launchpad by',
        u'%s (%s)' % (sender_name, canonical_url(sender)),
        u'%s.',
        u'For more information see',
        u'https://help.launchpad.net/YourAccount/ContactingPeople',
    ])
    # Craft and send one message per recipient.
    mailwrapper = MailWrapper(width=72)
    message = None
    for recipient_email, recipient in recipients_set.getRecipientPersons():
        recipient_name = str(encode(recipient.displayname))
        reason, rationale_header = recipients_set.getReason(recipient_email)
        reason = str(encode(reason)).replace('\n ', '\n')
        formatted_body = mailwrapper.format(body, force_wrap=True)
        formatted_body += additions % reason
        formatted_body = formatted_body.encode(charset)
        message = MIMEText(formatted_body, _charset=charset)
        message['From'] = formataddr((sender_name, sender_email))
        message['To'] = formataddr((recipient_name, recipient_email))
        message['Subject'] = subject_header
        message['Message-ID'] = make_msgid('launchpad')
        message['X-Launchpad-Message-Rationale'] = rationale_header
        message['X-Launchpad-Message-For'] = person_or_team.name
        # Send the message.
        sendmail(message, bulk=False)
    # Use the information from the last message sent to record the action
    # taken. The record will be used to throttle user-to-user emails.
    if message is not None:
        authorization.record(message)
def send_direct_contact_email(
    sender_email, recipients_set, subject, body):
    """Send a direct user-to-user email.

    :param sender_email: The email address of the sender.
    :type sender_email: string
    :param recipients_set: The recipients.
    :type recipients_set:' A ContactViaWebNotificationSet
    :param subject: The Subject header.
    :type subject: unicode
    :param body: The message body.
    :type body: unicode
    :return: The sent message.
    :rtype: `email.Message.Message`
    """
    # Craft the email message.  Start by checking whether the subject and
    # message bodies are ASCII or not.
    subject_header = encode(subject)
    try:
        body.encode('us-ascii')
        charset = 'us-ascii'
    except UnicodeEncodeError:
        charset = 'utf-8'
    # Get the sender's real name, encoded as per RFC 2047.
    person_set = getUtility(IPersonSet)
    sender = person_set.getByEmail(sender_email)
    assert sender is not None, 'No person for sender %s' % sender_email
    sender_name = str(encode(sender.displayname))
    # Do a single authorization/quota check for the sender.  We consume one
    # quota credit per contact, not per recipient.
    authorization = IDirectEmailAuthorization(sender)
    if not authorization.is_allowed:
        raise QuotaReachedError(sender.displayname, authorization)
    # Add the footer as a unicode string, then encode the body if necessary.
    # This is not entirely optimal if the body has non-ascii characters in it,
    # since the footer may get garbled in a non-MIME aware mail reader.  Who
    # uses those anyway!?  The only alternative is to attach the footer as a
    # MIME attachment with a us-ascii charset, but that has it's own set of
    # problems (and user complaints).  Email sucks.
    additions = u'\n'.join([
        u'',
        u'-- ',
        u'This message was sent from Launchpad by',
        u'%s (%s)' % (sender_name, canonical_url(sender)),
        u'%s.',
        u'For more information see',
        u'https://help.launchpad.net/YourAccount/ContactingPeople',
        ])
    # Craft and send one message per recipient.
    mailwrapper = MailWrapper(width=72)
    message = None
    for recipient_email, recipient in recipients_set.getRecipientPersons():
        recipient_name = str(encode(recipient.displayname))
        reason, rational_header = recipients_set.getReason(recipient_email)
        reason = str(encode(reason)).replace('\n ', '\n')
        formatted_body = mailwrapper.format(body, force_wrap=True)
        formatted_body += additions % reason
        formatted_body = formatted_body.encode(charset)
        message = MIMEText(formatted_body, _charset=charset)
        message['From'] = formataddr((sender_name, sender_email))
        message['To'] = formataddr((recipient_name, recipient_email))
        message['Subject'] = subject_header
        message['Message-ID'] = make_msgid('launchpad')
        message['X-Launchpad-Message-Rationale'] = rational_header
        # Send the message.
        sendmail(message, bulk=False)
    # Use the information from the last message sent to record the action
    # taken. The record will be used to throttle user-to-user emails.
    if message is not None:
        authorization.record(message)