Esempio n. 1
0
def autorespond_to_sender(mlist, sender, language=None):
    """Should Mailman automatically respond to this sender?

    :param mlist: The mailing list.
    :type mlist: `IMailingList`.
    :param sender: The sender's email address.
    :type sender: string
    :param language: Optional language.
    :type language: `ILanguage` or None
    :return: True if an automatic response should be sent, otherwise False.
        If an automatic response is not sent, a message is sent indicating
        that, er no more will be sent today.
    :rtype: bool
    """
    if language is None:
        language = mlist.preferred_language
    max_autoresponses_per_day = int(config.mta.max_autoresponses_per_day)
    if max_autoresponses_per_day == 0:
        # Unlimited.
        return True
    # Get an IAddress from an email address.
    user_manager = getUtility(IUserManager)
    address = user_manager.get_address(sender)
    if address is None:
        address = user_manager.create_address(sender)
    response_set = IAutoResponseSet(mlist)
    todays_count = response_set.todays_count(address, Response.hold)
    if todays_count < max_autoresponses_per_day:
        # This person has not reached their automatic response limit, so it's
        # okay to send a response.
        response_set.response_sent(address, Response.hold)
        return True
    elif todays_count == max_autoresponses_per_day:
        # The last one we sent was the last one we should send today.  Instead
        # of sending an automatic response, send them the "no more today"
        # message.
        log.info('hold autoresponse limit hit: %s', sender)
        response_set.response_sent(address, Response.hold)
        # Send this notification message instead.
        text = make(
            'nomoretoday.txt',
            language=language.code,
            sender=sender,
            listname=mlist.fqdn_listname,
            count=todays_count,
            owneremail=mlist.owner_address,
        )
        with _.using(language.code):
            msg = UserNotification(
                sender,
                mlist.owner_address,
                _('Last autoresponse notification for today'),
                text,
                lang=language)
        msg.send(mlist)
        return False
    else:
        # We've sent them everything we're going to send them today.
        log.info('Automatic response limit discard: %s', sender)
        return False
Esempio n. 2
0
def autorespond_to_sender(mlist, sender, language=None):
    """Should Mailman automatically respond to this sender?

    :param mlist: The mailing list.
    :type mlist: `IMailingList`.
    :param sender: The sender's email address.
    :type sender: string
    :param language: Optional language.
    :type language: `ILanguage` or None
    :return: True if an automatic response should be sent, otherwise False.
        If an automatic response is not sent, a message is sent indicating
        that, er no more will be sent today.
    :rtype: bool
    """
    if language is None:
        language = mlist.preferred_language
    max_autoresponses_per_day = int(config.mta.max_autoresponses_per_day)
    if max_autoresponses_per_day == 0:
        # Unlimited.
        return True
    # Get an IAddress from an email address.
    user_manager = getUtility(IUserManager)
    address = user_manager.get_address(sender)
    if address is None:
        address = user_manager.create_address(sender)
    response_set = IAutoResponseSet(mlist)
    todays_count = response_set.todays_count(address, Response.hold)
    if todays_count < max_autoresponses_per_day:
        # This person has not reached their automatic response limit, so it's
        # okay to send a response.
        response_set.response_sent(address, Response.hold)
        return True
    elif todays_count == max_autoresponses_per_day:
        # The last one we sent was the last one we should send today.  Instead
        # of sending an automatic response, send them the "no more today"
        # message.
        log.info('hold autoresponse limit hit: %s', sender)
        response_set.response_sent(address, Response.hold)
        # Send this notification message instead.
        text = make('nomoretoday.txt',
                    language=language.code,
                    sender=sender,
                    listname=mlist.fqdn_listname,
                    count=todays_count,
                    owneremail=mlist.owner_address,
                    )
        with _.using(language.code):
            msg = UserNotification(
                sender, mlist.owner_address,
                _('Last autoresponse notification for today'),
                text, lang=language)
        msg.send(mlist)
        return False
    else:
        # We've sent them everything we're going to send them today.
        log.info('Automatic response limit discard: %s', sender)
        return False
Esempio n. 3
0
 def test_delete_list_with_autoresponse_record(self):
     # Ensure that mailing lists with auto-response sets can be deleted.  In
     # issue #115, this fails under PostgreSQL, but not SQLite.
     list_manager = getUtility(IListManager)
     user_manager = getUtility(IUserManager)
     mlist = create_list('*****@*****.**')
     address = user_manager.create_address('*****@*****.**')
     autoresponse_set = IAutoResponseSet(mlist)
     autoresponse_set.response_sent(address, Response.hold)
     list_manager.delete(mlist)
     self.assertIsNone(list_manager.get('*****@*****.**'))
Esempio n. 4
0
 def test_delete_list_with_autoresponse_record(self):
     # Ensure that mailing lists with auto-response sets can be deleted.  In
     # issue #115, this fails under PostgreSQL, but not SQLite.
     list_manager = getUtility(IListManager)
     user_manager = getUtility(IUserManager)
     mlist = create_list('*****@*****.**')
     address = user_manager.create_address('*****@*****.**')
     autoresponse_set = IAutoResponseSet(mlist)
     autoresponse_set.response_sent(address, Response.hold)
     list_manager.delete(mlist)
     self.assertIsNone(list_manager.get('*****@*****.**'))
Esempio n. 5
0
 def test_delete_address(self):
     address = self._usermanager.create_address('*****@*****.**')
     address.verified_on = now()
     # Subscribe the address to a list.
     mlist = create_list('*****@*****.**')
     mlist.subscribe(address)
     # Set an autorespond record.
     response_set = IAutoResponseSet(mlist)
     response_set.response_sent(address, Response.hold)
     # And add a digest record.
     mlist.send_one_last_digest_to(address, DeliveryMode.plaintext_digests)
     # Now delete the address.
     self._usermanager.delete_address(address)
     # Flush the database to provoke an integrity error on PostgreSQL
     # without the fix.
     config.db.store.flush()
     self.assertIsNone(self._usermanager.get_address('*****@*****.**'))
Esempio n. 6
0
 def test_delete_address(self):
     address = self._usermanager.create_address('*****@*****.**')
     address.verified_on = now()
     # Subscribe the address to a list.
     mlist = create_list('*****@*****.**')
     mlist.subscribe(address)
     # Set an autorespond record.
     response_set = IAutoResponseSet(mlist)
     response_set.response_sent(address, Response.hold)
     # And add a digest record.
     mlist.send_one_last_digest_to(address, DeliveryMode.plaintext_digests)
     # Now delete the address.
     self._usermanager.delete_address(address)
     # Flush the database to provoke an integrity error on PostgreSQL
     # without the fix.
     config.db.store.flush()
     self.assertIsNone(self._usermanager.get_address('*****@*****.**'))
Esempio n. 7
0
    def test_max_autoresponses_per_day(self):
        # The last one we sent was the last one we should send today.  Instead
        # of sending an automatic response, send them the "no more today"
        # message.
        config.push('max-1', """
        [mta]
        max_autoresponses_per_day: 1
        """)
        # Simulate a response having been sent to an address already.
        anne = getUtility(IUserManager).create_address('*****@*****.**')
        response_set = IAutoResponseSet(self._mlist)
        response_set.response_sent(anne, Response.hold)
        # Trigger the sending of a "last response for today" using the default
        # language (i.e. the mailing list's preferred language).
        autorespond_to_sender(self._mlist, '*****@*****.**')
        # So first, there should be one more hold response sent to the user.
        self.assertEqual(response_set.todays_count(anne, Response.hold), 2)
        # And the virgin queue should have the message in it.
        messages = get_queue_messages('virgin')
        self.assertEqual(len(messages), 1)
        # Remove the variable headers.
        message = messages[0].msg
        self.assertTrue('message-id' in message)
        del message['message-id']
        self.assertTrue('date' in message)
        del message['date']
        self.eq(messages[0].msg.as_string(), """\
MIME-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Subject: Last autoresponse notification for today
From: [email protected]
To: [email protected]
Precedence: bulk

We have received a message from your address <*****@*****.**>
requesting an automated response from the [email protected] mailing
list.

The number we have seen today: 1.  In order to avoid problems such as
mail loops between email robots, we will not be sending you any
further responses today.  Please try again tomorrow.

If you believe this message is in error, or if you have any questions,
please contact the list owner at [email protected].""")
Esempio n. 8
0
    def test_max_autoresponses_per_day(self):
        # The last one we sent was the last one we should send today.  Instead
        # of sending an automatic response, send them the "no more today"
        # message.  Start by simulating a response having been sent to an
        # address already.
        anne = getUtility(IUserManager).create_address('*****@*****.**')
        response_set = IAutoResponseSet(self._mlist)
        response_set.response_sent(anne, Response.hold)
        # Trigger the sending of a "last response for today" using the default
        # language (i.e. the mailing list's preferred language).
        autorespond_to_sender(self._mlist, '*****@*****.**')
        # So first, there should be one more hold response sent to the user.
        self.assertEqual(response_set.todays_count(anne, Response.hold), 2)
        # And the virgin queue should have the message in it.
        messages = get_queue_messages('virgin')
        self.assertEqual(len(messages), 1)
        # Remove the variable headers.
        message = messages[0].msg
        self.assertIn('message-id', message)
        del message['message-id']
        self.assertIn('date', message)
        del message['date']
        self.assertMultiLineEqual(
            messages[0].msg.as_string(), """\
MIME-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Subject: Last autoresponse notification for today
From: [email protected]
To: [email protected]
Precedence: bulk

We have received a message from your address <*****@*****.**>
requesting an automated response from the [email protected] mailing
list.

The number we have seen today: 1.  In order to avoid problems such as
mail loops between email robots, we will not be sending you any
further responses today.  Please try again tomorrow.

If you believe this message is in error, or if you have any questions,
please contact the list owner at [email protected].""")
Esempio n. 9
0
 def process(self, mlist, msg, msgdata):
     """See `IHandler`."""
     # There are several cases where the replybot is short-circuited:
     # * the original message has an "X-Ack: No" header
     # * the message has a Precedence header with values bulk, junk, or
     #   list, and there's no explicit "X-Ack: yes" header
     # * the message metadata has a true 'noack' key
     ack = msg.get('x-ack', '').lower()
     if ack == 'no' or msgdata.get('noack'):
         return
     precedence = msg.get('precedence', '').lower()
     if ack != 'yes' and precedence in ('bulk', 'junk', 'list'):
         return
     # Check to see if the list is even configured to autorespond to this
     # email message.  Note: the incoming message processors should set the
     # destination key in the message data.
     if msgdata.get('to_owner'):
         if mlist.autorespond_owner is ResponseAction.none:
             return
         response_type = Response.owner
         response_text = mlist.autoresponse_owner_text
     elif msgdata.get('to_request'):
         if mlist.autorespond_requests is ResponseAction.none:
             return
         response_type = Response.command
         response_text = mlist.autoresponse_request_text
     elif msgdata.get('to_list'):
         if mlist.autorespond_postings is ResponseAction.none:
             return
         response_type = Response.postings
         response_text = mlist.autoresponse_postings_text
     else:
         # There are no automatic responses for any other destination.
         return
     # Now see if we're in the grace period for this sender.  grace_period
     # = 0 means always automatically respond, as does an "X-Ack: yes"
     # header (useful for debugging).
     response_set = IAutoResponseSet(mlist)
     user_manager = getUtility(IUserManager)
     address = user_manager.get_address(msg.sender)
     if address is None:
         address = user_manager.create_address(msg.sender)
     grace_period = mlist.autoresponse_grace_period
     if grace_period > ALWAYS_REPLY and ack != 'yes':
         last = response_set.last_response(address, response_type)
         if last is not None and last.date_sent + grace_period > today():
             return
     # Okay, we know we're going to respond to this sender, craft the
     # message, send it, and update the database.
     display_name = mlist.display_name
     subject = _(
         'Auto-response for your message to the "$display_name" '
         'mailing list')
     # Do string interpolation into the autoresponse text
     d = dict(list_name = mlist.list_name,
              display_name = display_name,
              listurl = mlist.script_url('listinfo'),
              requestemail = mlist.request_address,
              owneremail = mlist.owner_address,
              )
     # Interpolation and Wrap the response text.
     text = wrap(expand(response_text, d))
     outmsg = UserNotification(msg.sender, mlist.bounces_address,
                               subject, text, mlist.preferred_language)
     outmsg['X-Mailer'] = _('The Mailman Replybot')
     # prevent recursions and mail loops!
     outmsg['X-Ack'] = 'No'
     outmsg.send(mlist)
     response_set.response_sent(address, response_type)
Esempio n. 10
0
 def process(self, mlist, msg, msgdata):
     """See `IHandler`."""
     # There are several cases where the replybot is short-circuited:
     # * the original message has an "X-Ack: No" header
     # * the message has a Precedence header with values bulk, junk, or
     #   list, and there's no explicit "X-Ack: yes" header
     # * the message metadata has a true 'noack' key
     ack = msg.get('x-ack', '').lower()
     if ack == 'no' or msgdata.get('noack'):
         return
     precedence = msg.get('precedence', '').lower()
     if ack != 'yes' and precedence in ('bulk', 'junk', 'list'):
         return
     # Check to see if the list is even configured to autorespond to this
     # email message.  Note: the incoming message processors should set the
     # destination key in the message data.
     if msgdata.get('to_owner'):
         if mlist.autorespond_owner is ResponseAction.none:
             return
         response_type = Response.owner
         response_text = mlist.autoresponse_owner_text
     elif msgdata.get('to_request'):
         if mlist.autorespond_requests is ResponseAction.none:
             return
         response_type = Response.command
         response_text = mlist.autoresponse_request_text
     elif msgdata.get('to_list'):
         if mlist.autorespond_postings is ResponseAction.none:
             return
         response_type = Response.postings
         response_text = mlist.autoresponse_postings_text
     else:
         # There are no automatic responses for any other destination.
         return
     # Now see if we're in the grace period for this sender.  grace_period
     # = 0 means always automatically respond, as does an "X-Ack: yes"
     # header (useful for debugging).
     response_set = IAutoResponseSet(mlist)
     user_manager = getUtility(IUserManager)
     address = user_manager.get_address(msg.sender)
     if address is None:
         address = user_manager.create_address(msg.sender)
     grace_period = mlist.autoresponse_grace_period
     if grace_period > ALWAYS_REPLY and ack != 'yes':
         last = response_set.last_response(address, response_type)
         if last is not None and last.date_sent + grace_period > today():
             return
     # Okay, we know we're going to respond to this sender, craft the
     # message, send it, and update the database.
     display_name = mlist.display_name
     subject = _('Auto-response for your message to the "$display_name" '
                 'mailing list')
     # Do string interpolation into the autoresponse text
     d = dict(
         list_name=mlist.list_name,
         display_name=display_name,
         requestemail=mlist.request_address,
         owneremail=mlist.owner_address,
     )
     # Interpolation and Wrap the response text.
     text = wrap(expand(response_text, mlist, d))
     outmsg = UserNotification(msg.sender, mlist.bounces_address, subject,
                               text, mlist.preferred_language)
     outmsg['X-Mailer'] = _('The Mailman Replybot')
     # prevent recursions and mail loops!
     outmsg['X-Ack'] = 'No'
     outmsg.send(mlist)
     response_set.response_sent(address, response_type)