Example #1
0
def process(mlist, msg, msgdata):
    """Check the standing of a non-Launchpad member.

    A message posted to a mailing list from a Launchpad member in good
    standing is allowed onto the list even if they are not members of the
    list.

    Because this handler comes before the standard Moderate handler, if the
    sender is not in good standing, we just defer to other decisions further
    along the pipeline.  If the sender is in good standing, we approve it.
    """
    sender = msg.get_sender()
    # Ask Launchpad about the standing of this member.
    in_good_standing = False
    proxy = XMLRPCRunner.get_mailing_list_api_proxy()
    # This will fail if we can't talk to Launchpad.  That's okay though
    # because Mailman's IncomingRunner will re-queue the message and re-start
    # processing at this handler.
    try:
        in_good_standing = proxy.inGoodStanding(sender)
    except Exception as error:
        XMLRPCRunner.handle_proxy_error(error, msg, msgdata)
    # If the sender is a member in good standing, that's all we need to know
    # in order to let the message pass.
    if in_good_standing:
        msgdata['approved'] = True
Example #2
0
def process(mlist, msg, msgdata):
    """Check the standing of a non-Launchpad member.

    A message posted to a mailing list from a Launchpad member in good
    standing is allowed onto the list even if they are not members of the
    list.

    Because this handler comes before the standard Moderate handler, if the
    sender is not in good standing, we just defer to other decisions further
    along the pipeline.  If the sender is in good standing, we approve it.
    """
    sender = msg.get_sender()
    # Ask Launchpad about the standing of this member.
    in_good_standing = False
    proxy = XMLRPCRunner.get_mailing_list_api_proxy()
    # This will fail if we can't talk to Launchpad.  That's okay though
    # because Mailman's IncomingRunner will re-queue the message and re-start
    # processing at this handler.
    try:
        in_good_standing = proxy.inGoodStanding(sender)
    except Exception as error:
        XMLRPCRunner.handle_proxy_error(error, msg, msgdata)
    # If the sender is a member in good standing, that's all we need to know
    # in order to let the message pass.
    if in_good_standing:
        msgdata['approved'] = True
Example #3
0
def hold(mlist, msg, msgdata, annotation):
    """Hold the message in both Mailman and Launchpad.

    `annotation` is an arbitrary string required by the API.
    """
    # Hold the message in Mailman and Launchpad so that it's easier to
    # resubmit it after approval via the LP u/i.  If the team administrator
    # ends up rejecting the message, it will also be easy to discard it on the
    # Mailman side.  But this way, we don't have to reconstitute the message
    # from the librarian if it gets approved.  However, unlike the standard
    # Moderate handler, we don't craft all the notification messages about
    # this hold.  We also need to keep track of the message-id (which better
    # be unique) because that's how we communicate about the message's status.
    request_id = mlist.HoldMessage(msg, annotation, msgdata)
    assert mlist.Locked(), ('Mailing list should be locked: %s' %
                            mlist.internal_name())
    # This is a hack because by default Mailman cannot look up held messages
    # by message-id.  This works because Mailman's persistency layer simply
    # pickles the MailList object, mostly without regard to a known schema.
    #
    # Mapping: message-id -> request-id
    holds = getattr(mlist, 'held_message_ids', None)
    if holds is None:
        holds = mlist.held_message_ids = {}
    message_id = msg.get('message-id')
    if message_id is None:
        msg['Message-ID'] = message_id = make_msgid()
    if message_id in holds:
        # No legitimate sender should ever give us a message with a duplicate
        # message id, so treat this as spam.
        syslog('vette', 'Discarding duplicate held message-id: %s', message_id)
        raise Errors.DiscardMessage
    # Discard messages that claim to be from the list itself because Mailman's
    # internal handlers did not approve the message before it arrived at this
    # step--these messages are forgeries.
    list_address = mlist.getListAddress()
    for sender in msg.get_senders():
        if list_address == sender:
            syslog('vette', 'Discarding forged message-id: %s', message_id)
            raise Errors.DiscardMessage
    # Discard messages without text content since there will be nothing to
    # moderate. Most of these messages are spam.
    if is_message_empty(msg):
        syslog('vette', 'Discarding text-less message-id: %s', message_id)
        raise Errors.DiscardMessage
    holds[message_id] = request_id
    # In addition to Message-ID, the librarian requires a Date header.
    if 'date' not in msg:
        msg['Date'] = formatdate()
    # Store the message in the librarian.
    proxy = XMLRPCRunner.get_mailing_list_api_proxy()
    # This will fail if we can't talk to Launchpad.  That's okay though
    # because Mailman's IncomingRunner will re-queue the message and re-start
    # processing at this handler.
    proxy.holdMessage(mlist.internal_name(), xmlrpclib.Binary(msg.as_string()))
    syslog('vette', 'Holding message for LP approval: %s', message_id)
    # Raise this exception, signaling to the incoming queue runner that it is
    # done processing this message, and should not send it through any further
    # handlers.
    raise Errors.HoldMessage
Example #4
0
def process(mlist, msg, msgdata):
    """Discard the message if it doesn't come from a Launchpad member."""
    if msgdata.get('approved'):
        return
    # Some automated processes will send messages to the mailing list. For
    # example, if the list is a contact address for a team and that team is
    # the contact address for a project's answer tracker, an automated message
    # will be sent from Launchpad. Check for a header that indicates this was
    # a Launchpad-generated message. See
    # lp.services.mail.sendmail.sendmail for where this is set.
    secret = msg['x-launchpad-hash']
    message_id = msg['message-id']
    if secret and message_id:
        hash = hashlib.sha1(mm_cfg.LAUNCHPAD_SHARED_SECRET)
        hash.update(message_id)
        if secret == hash.hexdigest():
            # Since this message is coming from Launchpad, pre-approve it.
            # Yes, this could be spoofed, but there's really no other way
            # (currently) to do it.
            msgdata['approved'] = True
            return
    # Ask Launchpad whether the sender is a Launchpad member.  If not, discard
    # the message with extreme prejudice, but log this.
    sender = msg.get_sender()
    # Check with Launchpad about whether the sender is a member or not.  If we
    # can't talk to Launchpad, I believe it's better to let the message get
    # posted to the list than to discard or hold it.
    is_member = True
    proxy = proxy = XMLRPCRunner.get_mailing_list_api_proxy()
    # This will fail if we can't talk to Launchpad.  That's okay though
    # because Mailman's IncomingRunner will re-queue the message and re-start
    # processing at this handler.
    try:
        is_member = proxy.isRegisteredInLaunchpad(sender)
    except Exception as error:
        XMLRPCRunner.handle_proxy_error(error, msg, msgdata)
    # This handler can just return if the sender is a member of Launchpad.
    if is_member:
        return
    # IncomingRunner already posts the Message-ID to the logs/vette for
    # discarded messages, so we only need to add a little more detail here.
    syslog('vette', 'Sender is not a Launchpad member: %s', sender)
    raise Errors.DiscardMessage
Example #5
0
def process(mlist, msg, msgdata):
    """Discard the message if it doesn't come from a Launchpad member."""
    if msgdata.get('approved'):
        return
    # Some automated processes will send messages to the mailing list. For
    # example, if the list is a contact address for a team and that team is
    # the contact address for a project's answer tracker, an automated message
    # will be sent from Launchpad. Check for a header that indicates this was
    # a Launchpad-generated message. See
    # lp.services.mail.sendmail.sendmail for where this is set.
    secret = msg['x-launchpad-hash']
    message_id = msg['message-id']
    if secret and message_id:
        hash = hashlib.sha1(mm_cfg.LAUNCHPAD_SHARED_SECRET)
        hash.update(message_id)
        if secret == hash.hexdigest():
            # Since this message is coming from Launchpad, pre-approve it.
            # Yes, this could be spoofed, but there's really no other way
            # (currently) to do it.
            msgdata['approved'] = True
            return
    # Ask Launchpad whether the sender is a Launchpad member.  If not, discard
    # the message with extreme prejudice, but log this.
    sender = msg.get_sender()
    # Check with Launchpad about whether the sender is a member or not.  If we
    # can't talk to Launchpad, I believe it's better to let the message get
    # posted to the list than to discard or hold it.
    is_member = True
    proxy = proxy = XMLRPCRunner.get_mailing_list_api_proxy()
    # This will fail if we can't talk to Launchpad.  That's okay though
    # because Mailman's IncomingRunner will re-queue the message and re-start
    # processing at this handler.
    try:
        is_member = proxy.isRegisteredInLaunchpad(sender)
    except Exception as error:
        XMLRPCRunner.handle_proxy_error(error, msg, msgdata)
    # This handler can just return if the sender is a member of Launchpad.
    if is_member:
        return
    # IncomingRunner already posts the Message-ID to the logs/vette for
    # discarded messages, so we only need to add a little more detail here.
    syslog('vette', 'Sender is not a Launchpad member: %s', sender)
    raise Errors.DiscardMessage
Example #6
0
    def raise_proxy_exception(self, method_name):
        """Raise an exception when calling the passed proxy method name."""
        def raise_exception(*args):
            raise Exception('Test exception handling.')

        proxy = XMLRPCRunner.get_mailing_list_api_proxy()
        original_method = getattr(proxy.__class__, method_name)
        setattr(proxy.__class__, method_name, raise_exception)
        try:
            yield
        finally:
            setattr(proxy.__class__, method_name, original_method)
Example #7
0
    def raise_proxy_exception(self, method_name):
        """Raise an exception when calling the passed proxy method name."""

        def raise_exception(*args):
            raise Exception('Test exception handling.')

        proxy = XMLRPCRunner.get_mailing_list_api_proxy()
        original_method = getattr(proxy.__class__, method_name)
        setattr(proxy.__class__, method_name, raise_exception)
        try:
            yield
        finally:
            setattr(proxy.__class__, method_name, original_method)
Example #8
0
def hold(mlist, msg, msgdata, annotation):
    """Hold the message in both Mailman and Launchpad.

    `annotation` is an arbitrary string required by the API.
    """
    # Hold the message in Mailman and Launchpad so that it's easier to
    # resubmit it after approval via the LP u/i.  If the team administrator
    # ends up rejecting the message, it will also be easy to discard it on the
    # Mailman side.  But this way, we don't have to reconstitute the message
    # from the librarian if it gets approved.  However, unlike the standard
    # Moderate handler, we don't craft all the notification messages about
    # this hold.  We also need to keep track of the message-id (which better
    # be unique) because that's how we communicate about the message's status.
    request_id = mlist.HoldMessage(msg, annotation, msgdata)
    assert mlist.Locked(), (
        'Mailing list should be locked: %s' % mlist.internal_name())
    # This is a hack because by default Mailman cannot look up held messages
    # by message-id.  This works because Mailman's persistency layer simply
    # pickles the MailList object, mostly without regard to a known schema.
    #
    # Mapping: message-id -> request-id
    holds = getattr(mlist, 'held_message_ids', None)
    if holds is None:
        holds = mlist.held_message_ids = {}
    message_id = msg.get('message-id')
    if message_id is None:
        msg['Message-ID'] = message_id = make_msgid()
    if message_id in holds:
        # No legitimate sender should ever give us a message with a duplicate
        # message id, so treat this as spam.
        syslog('vette',
               'Discarding duplicate held message-id: %s', message_id)
        raise Errors.DiscardMessage
    # Discard messages that claim to be from the list itself because Mailman's
    # internal handlers did not approve the message before it arrived at this
    # step--these messages are forgeries.
    list_address = mlist.getListAddress()
    for sender in msg.get_senders():
        if list_address == sender:
            syslog('vette',
                   'Discarding forged message-id: %s', message_id)
            raise Errors.DiscardMessage
    # Discard messages without text content since there will be nothing to
    # moderate. Most of these messages are spam.
    if is_message_empty(msg):
        syslog('vette',
               'Discarding text-less message-id: %s', message_id)
        raise Errors.DiscardMessage
    holds[message_id] = request_id
    # In addition to Message-ID, the librarian requires a Date header.
    if 'date' not in msg:
        msg['Date'] = formatdate()
    # Store the message in the librarian.
    proxy = XMLRPCRunner.get_mailing_list_api_proxy()
    # This will fail if we can't talk to Launchpad.  That's okay though
    # because Mailman's IncomingRunner will re-queue the message and re-start
    # processing at this handler.
    proxy.holdMessage(mlist.internal_name(),
                      xmlrpclib.Binary(msg.as_string()))
    syslog('vette', 'Holding message for LP approval: %s', message_id)
    # Raise this exception, signaling to the incoming queue runner that it is
    # done processing this message, and should not send it through any further
    # handlers.
    raise Errors.HoldMessage