def assert_forward_content_equal(self, content):
     """
     Asserts that the content of the forwarded message is equal to the given
     ``content``.
     """
     msg = mail.outbox[0].message()
     self.assertEqual(get_decoded_message_payload(msg), content)
Exemplo n.º 2
0
def bounce_is_for_spam(message):
    spam_bounce_re = [
        # Google blocks executables files
        # 552-5.7.0 This message was blocked because its content presents a[...]
        # 552-5.7.0 security issue. Please visit
        # 552-5.7.0  https://support.google.com/mail/?p=BlockedMessage to [...]
        # 552 5.7.0 message content and attachment content guidelines. [...]
        r"552-5.7.0 This message was blocked",
        # host ...: 550 High probability of spam
        # host ...: 554 5.7.1 Message rejected because it contains malware
        # 550 Executable files are not allowed in compressed files.
        r"55[0-9] .*(?:spam|malware|virus|[Ee]xecutable files)",
    ]
    # XXX: Handle delivery report properly
    for part in message.walk():
        if not part or part.is_multipart():
            continue
        text = get_decoded_message_payload(part)
        if text is None:
            continue
        for line in text.splitlines()[0:15]:
            for rule in spam_bounce_re:
                if re.search(rule, line):
                    return True

    return False
Exemplo n.º 3
0
def create_dak_rm_news(message, package, body=None, version=''):
    """Create a :class:`News` out of a removal email sent by DAK."""
    if not body:
        body = get_decoded_message_payload(message)
    suite = re.search(r"have been removed from (\S+):", body).group(1)
    title = "Removed {ver} from {suite}".format(ver=version, suite=suite)
    return mail_news.create_news(message, package, title=title)
Exemplo n.º 4
0
def _get_message_body(msg):
    """
    Returns the message body, joining together all parts into one string.

    :param msg: The original received package message
    :type msg: :py:class:`email.message.Message`
    """
    return '\n'.join(get_decoded_message_payload(part)
                     for part in msg.walk() if not part.is_multipart())
Exemplo n.º 5
0
def process(message):
    """
    The function which actually processes a received command email message.

    :param message: The received command email message.
    :type message: ``bytes``
    """
    assert isinstance(message, six.binary_type), 'Message must be given as bytes'
    msg = message_from_bytes(message)
    email = extract_email_address_from_header(msg.get('From', ''))
    logdata = {
        'from': email,
        'msgid': msg.get('Message-ID', 'no-msgid-present@localhost'),
    }
    logger.info("control <= %(from)s %(msgid)s", logdata)
    if 'X-Loop' in msg and DISTRO_TRACKER_CONTROL_EMAIL in msg.get_all('X-Loop'):
        logger.info("control :: discarded %(msgid)s due to X-Loop", logdata)
        return
    # Get the first plain-text part of the message
    plain_text_part = next(typed_subpart_iterator(msg, 'text', 'plain'), None)
    if not plain_text_part:
        # There is no plain text in the email
        send_plain_text_warning(msg, logdata)
        return

    # Decode the plain text into a unicode string
    try:
        text = get_decoded_message_payload(plain_text_part)
    except UnicodeDecodeError:
        send_plain_text_warning(msg, logdata)
        return

    lines = extract_command_from_subject(msg) + text.splitlines()
    # Process the commands
    factory = CommandFactory({'email': email})
    confirmation_set = ConfirmationSet()
    processor = CommandProcessor(factory)
    processor.confirmation_set = confirmation_set
    processor.process(lines)

    confirmation_set.ask_confirmation_all()
    # Send a response only if there were some commands processed
    if processor.is_success():
        send_response(msg, processor.get_output(), recipient_email=email,
                      cc=set(confirmation_set.get_emails()))
    else:
        logger.info("control :: no command processed in %(msgid)s", logdata)
Exemplo n.º 6
0
def _create_news_from_dak_email(message):
    x_dak = message['X-DAK']
    katie = x_dak.split()[1]

    if katie != 'rm':
        # Only rm mails are processed.
        return

    body = get_decoded_message_payload(message)
    if not body:
        # The content cannot be decoded.
        return
    # Find all lines giving information about removed source packages
    re_rmline = re.compile(r"^\s*(\S+)\s*\|\s*(\S+)\s*\|.*source", re.M)
    source_removals = re_rmline.findall(body)
    # Add a news item for each source removal.
    created_news = []
    for removal in source_removals:
        package_name, version = removal
        created_news.append(
            create_dak_rm_news(message, package_name, body=body,
                               version=version))
    return created_news
Exemplo n.º 7
0
def create_news_from_email_message(message):
    """
    In Debian's implementation, this function creates news when the received
    mail's origin is either the testing watch or katie.
    """
    subject = message.get("Subject", None)
    if not subject:
        return
    subject_words = subject.split()

    # Source upload?
    if len(subject_words) > 1 and subject_words[0] in ('Accepted', 'Installed'):
        if 'source' not in subject:
            # Only source uploads should be considered.
            return
        package_name = subject_words[1]
        package = get_or_none(SourcePackageName, name=package_name)
        if package:
            return [EmailNews.objects.create_email_news(message, package)]
    # DAK rm?
    elif 'X-DAK' in message:
        x_dak = message['X-DAK']
        katie = x_dak.split()[1]

        if katie != 'rm':
            # Only rm mails are processed.
            return

        body = get_decoded_message_payload(message)
        if not body:
            # The content cannot be decoded.
            return
        # Find all lines giving information about removed source packages
        re_rmline = re.compile(r"^\s*(\S+)\s*\|\s*(\S+)\s*\|.*source", re.M)
        source_removals = re_rmline.findall(body)
        # Find the suite from which the packages have been removed
        suite = re.search(r"have been removed from (\S+):", body).group(1)
        news_from = message.get('Sender', '')
        # Add a news item for each source removal.
        created_news = []
        for removal in source_removals:
            package_name, version = removal
            package = get_or_none(SourcePackageName, name=package_name)
            if not package:
                # This package is not tracked
                continue
            title = "Removed {ver} from {suite}".format(ver=version, suite=suite)
            created_news.append(EmailNews.objects.create_email_news(
                title=title,
                message=message,
                package=package,
                created_by=news_from))
        return created_news
    # Testing Watch?
    elif 'X-Testing-Watch-Package' in message:
        package_name = message['X-Testing-Watch-Package']
        package = get_or_none(SourcePackageName, name=package_name)
        if not package:
            # This package is not tracked
            return
        title = message.get('Subject', '')
        if not title:
            title = 'Testing Watch Message'
        return [
            EmailNews.objects.create_email_news(
                title=title,
                message=message,
                package=package,
                created_by='Britney')
        ]