Example #1
0
    def test_signatures_added(self):
        """
        Tests that signatures are correctly added to the news which previously
        didn't have any, despite having signed content.
        """
        # Set up news based on a signed message.
        signed_news = []
        unsigned_news = []
        self.import_key_into_keyring('key1.pub')
        # The content of the test news item is found in a file
        file_path = self.get_test_data_path(
            'signed-message-quoted-printable')
        with open(file_path, 'rb') as f:
            content = f.read()
        expected_name = 'PTS Tests'
        expected_email = '*****@*****.**'
        # The first signed news has the same content as what is found
        # the signed test file.
        signed_news.append(EmailNews.objects.create_email_news(
            message=message_from_bytes(content),
            package=self.package))
        # For the second one, add some text after the signature: this
        # should still mean that the correct signature can be extracted!
        signed_news.append(EmailNews.objects.create_email_news(
            message=message_from_bytes(content + b'\nMore content'),
            package=self.package))
        # Set up some unsigned news.
        unsigned_news.append(EmailNews.objects.create_email_news(
            message=message_from_bytes(b'Subject: Hi\n\nPayload.'),
            package=self.package))
        # A non-email based news item
        unsigned_news.append(News.objects.create(
            package=self.package,
            content="Some content.."
        ))
        # Make sure that the signed news do not have associated
        # signature information
        for signed in signed_news:
            signed.signed_by.clear()

        # Run the command
        call_command("tracker_update_news_signatures")

        # The signed news items have associated signature information
        for signed in signed_news:
            self.assertEqual(1, signed.signed_by.count())
            signer = signed.signed_by.all()[0]
            # The signature is actually correct too?
            self.assertEqual(expected_name, signer.name)
            self.assertEqual(expected_email, signer.email)
        # The unsigned messages still do not have any signature info
        for unsigned in unsigned_news:
            self.assertEqual(0, unsigned.signed_by.count())
Example #2
0
    def load_mail_from_file(self, filename):
        """
        Load the mail to process from a file.

        :param str filename: Path of the file to parse as mail.
        """
        with open(filename, 'rb') as f:
            self.message = message_from_bytes(f.read())
    def test_news_feed_with_latin1_message_without_encoding(self):
        file_path = self.get_test_data_path('message-without-encoding')
        with open(file_path, 'rb') as f:
            content = f.read()
        EmailNews.objects.create_email_news(
            message=message_from_bytes(content),
            package=self.package)

        response = self.get_rss_feed_response(self.package.name)

        # Ensure the name is correctly decoded
        self.assertContains(response, 'David Martínez Moreno')
def process(message, sent_to_address=None):
    """
    Handles the dispatching of received messages.

    :param message: The received message
    :type message: ``bytes``

    :param sent_to_address: The address to which the message was sent.
        Necessary in order to determine which package it was sent to.
    """
    assert isinstance(message, six.binary_type), 'Message must be given as bytes'
    msg = message_from_bytes(message)

    logdata = {
        'from': extract_email_address_from_header(msg.get('From', '')),
        'msgid': msg.get('Message-ID', 'no-msgid-present@localhost'),
        'to': sent_to_address or '',
    }
    logger.info("dispatch <= %(from)s for %(to)s %(msgid)s", logdata)
    if sent_to_address is None:
        # No MTA was recognized, the last resort is to try and use the message
        # To header.
        sent_to_address = extract_email_address_from_header(msg['To'])

    if sent_to_address.startswith('bounces+'):
        return handle_bounces(sent_to_address)

    local_part = sent_to_address.split('@')[0]

    # Extract package name
    package_name = get_package_name(local_part)
    # Check loop
    package_email = '{package}@{distro_tracker_fqdn}'.format(
        package=package_name, distro_tracker_fqdn=DISTRO_TRACKER_FQDN)
    if package_email in msg.get_all('X-Loop', ()):
        # Bad X-Loop, discard the message
        logger.info('dispatch :: discarded %(msgid)s due to X-Loop', logdata)
        return

    # Extract keyword
    keyword = get_keyword(local_part, msg)
    logger.info('dispatch :: %s %s', package_name, keyword)
    # Default keywords require special approvement
    if keyword == 'default' and not approved_default(msg):
        logger.info('dispatch :: discarded non-approved message %(msgid)s',
                    logdata)
        return

    # Now send the message to subscribers
    add_new_headers(msg, package_name, keyword)
    send_to_subscribers(msg, package_name, keyword)
    send_to_teams(msg, package_name, keyword)
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)
Example #6
0
def process(message):
    """
    Process an incoming message which is potentially a news item.

    The function first tries to call the vendor-provided function
    :func:`create_news_from_email_message
    <distro_tracker.vendor.skeleton.rules.create_news_from_email_message>`.

    If this function does not exist a news item is created only if there is a
    ``X-Distro-Tracker-Package`` header set giving the name of an existing
    source or pseudo package.

    If the ``X-Distro-Tracker-Url`` is also set then the content of the message
    will not be the email content, rather the URL given in this header.

    :param message: The received message
    :type message: :class:`bytes`
    """
    assert isinstance(message, bytes), 'Message must be given as bytes'

    msg = message_from_bytes(message)

    # Try asking the vendor function first.
    created, implemented = vendor.call('create_news_from_email_message', msg)
    if implemented and created:
        return

    # If the message has an X-Distro-Tracker-Package header, it is
    # automatically made into a news item.
    if 'X-Distro-Tracker-Package' in msg:
        package_name = msg['X-Distro-Tracker-Package']
        package = get_or_none(PackageName, name=package_name)
        if not package:
            return
        if 'X-Distro-Tracker-Url' not in msg:
            create_news(msg, package)
        else:
            distro_tracker_url = msg['X-Distro-Tracker-Url']
            News.objects.create(
                title=distro_tracker_url,
                content="<a href={url}>{url}</a>".format(
                    url=escape(distro_tracker_url)),
                package=package,
                content_type='text/html')