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())
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)
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')