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 _get_logdata(msg, package, keyword): return { 'from': extract_email_address_from_header(msg.get('From', '')), 'msgid': msg.get('Message-ID', 'no-msgid-present@localhost'), 'package': package or '<unknown>', 'keyword': keyword or '<unknown>', }
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)