Example #1
0
def handle_one_mail(log, mail, file_alias, file_alias_url,
                    signature_timestamp_checker):
    """Process one message.

    Returns None when the message has either been successfully processed, or
    handled as a known error condition, in which case a reply will have been
    sent if appropriate.
    """
    log.debug('processing mail from %r message-id %r' %
              (mail['from'], mail['message-id']))

    # If the Return-Path header is '<>', it probably means
    # that it's a bounce from a message we sent.
    if mail['Return-Path'] == '<>':
        log.info("Message had an empty Return-Path.")
        return
    if mail.get_content_type() == 'multipart/report':
        # Mails with a content type of multipart/report are
        # generally DSN messages and should be ignored.
        log.info("Got a multipart/report message.")
        return
    if 'precedence' in mail:
        log.info("Got a message with a precedence header.")
        return

    if mail.raw_length > MAX_EMAIL_SIZE:
        complaint = (
            "The mail you sent to Launchpad is too long.\n\n"
            "Your message <%s>\nwas %d MB and the limit is %d MB." %
            (mail['message-id'], mail.raw_length / 1e6, MAX_EMAIL_SIZE / 1e6))
        log.info(complaint)
        # It's probably big and it's probably mostly binary, so trim it pretty
        # aggressively.
        send_process_error_notification(mail['From'],
                                        'Mail to Launchpad was too large',
                                        complaint,
                                        mail,
                                        max_return_size=8192)
        return

    try:
        principal = authenticateEmail(mail, signature_timestamp_checker)
    except (InvalidSignature, IncomingEmailError) as error:
        send_process_error_notification(mail['From'], 'Submit Request Failure',
                                        str(error), mail)
        return
    except InactiveAccount:
        log.info("Inactive account found for %s" % mail['From'])
        return

    addresses = extract_addresses(mail, file_alias_url, log)
    log.debug('mail was originally to: %r' % (addresses, ))

    try:
        do_paranoid_envelope_to_validation(addresses)
    except AssertionError as e:
        log.info("Invalid email address: %s" % e)
        return

    handler = None
    for email_addr in addresses:
        user, domain = email_addr.split('@')
        handler = mail_handlers.get(domain)
        if handler is not None:
            break
    else:
        raise AssertionError("No handler registered for '%s' " %
                             (', '.join(addresses)))

    if principal is None and not handler.allow_unknown_users:
        log.info('Mail from unknown users not permitted for this handler')
        return

    handled = handler.process(mail, email_addr, file_alias)
    if not handled:
        raise AssertionError("Handler found, but message was not handled")
Example #2
0
 def test_get(self):
     handler = mail_handlers.get(config.launchpad.code_domain)
     self.assertIsInstance(handler, CodeHandler)
Example #3
0
def handle_one_mail(log, mail, file_alias, file_alias_url,
                    signature_timestamp_checker):
    """Process one message.

    Returns None when the message has either been successfully processed, or
    handled as a known error condition, in which case a reply will have been
    sent if appropriate.
    """
    log.debug('processing mail from %r message-id %r' %
        (mail['from'], mail['message-id']))

    # If the Return-Path header is '<>', it probably means
    # that it's a bounce from a message we sent.
    if mail['Return-Path'] == '<>':
        log.info("Message had an empty Return-Path.")
        return
    if mail.get_content_type() == 'multipart/report':
        # Mails with a content type of multipart/report are
        # generally DSN messages and should be ignored.
        log.info("Got a multipart/report message.")
        return
    if 'precedence' in mail:
        log.info("Got a message with a precedence header.")
        return

    if mail.raw_length > MAX_EMAIL_SIZE:
        complaint = (
            "The mail you sent to Launchpad is too long.\n\n"
            "Your message <%s>\nwas %d MB and the limit is %d MB." %
            (mail['message-id'], mail.raw_length / 1e6, MAX_EMAIL_SIZE / 1e6))
        log.info(complaint)
        # It's probably big and it's probably mostly binary, so trim it pretty
        # aggressively.
        send_process_error_notification(
            mail['From'], 'Mail to Launchpad was too large', complaint,
            mail, max_return_size=8192)
        return

    try:
        principal = authenticateEmail(mail, signature_timestamp_checker)
    except (InvalidSignature, IncomingEmailError) as error:
        send_process_error_notification(
            mail['From'], 'Submit Request Failure', str(error), mail)
        return
    except InactiveAccount:
        log.info("Inactive account found for %s" % mail['From'])
        return

    addresses = extract_addresses(mail, file_alias_url, log)
    log.debug('mail was originally to: %r' % (addresses,))

    try:
        do_paranoid_envelope_to_validation(addresses)
    except AssertionError as e:
        log.info("Invalid email address: %s" % e)
        return

    handler = None
    for email_addr in addresses:
        user, domain = email_addr.split('@')
        handler = mail_handlers.get(domain)
        if handler is not None:
            break
    else:
        raise AssertionError(
            "No handler registered for '%s' " % (', '.join(addresses)))

    if principal is None and not handler.allow_unknown_users:
        log.info('Mail from unknown users not permitted for this handler')
        return

    handled = handler.process(mail, email_addr, file_alias)
    if not handled:
        raise AssertionError("Handler found, but message was not handled")
 def test_get(self):
     handler = mail_handlers.get(config.launchpad.code_domain)
     self.assertIsInstance(handler, CodeHandler)