def _send_email_oops(trans, log, mail, error_msg, file_alias_url): """Handle an error that generates an oops. It does the following: * records an OOPS with error_msg and file_alias_url * commits the current transaction to ensure that the message gets sent """ log.info('error processing mail: %s' % (error_msg, )) oops_id = report_oops(file_alias_url=file_alias_url, error_msg=error_msg) log.info('oops %s' % (oops_id, )) send_process_error_notification( mail['From'], 'Submit Request Failure', get_error_message('oops.txt', oops_id=oops_id), mail) trans.commit()
def processComment(self, mail, email_addr, file_alias): """Process an email and create a CodeReviewComment. The only mail command understood is 'vote', which takes 'approve', 'disapprove', or 'abstain' as values. Specifically, it takes any CodeReviewVote item value, case-insensitively. :return: True. """ user = getUtility(ILaunchBag).user try: merge_proposal = self.getBranchMergeProposal(email_addr) except NonExistantBranchMergeProposalAddress: send_process_error_notification( str(user.preferredemail.email), 'Submit Request Failure', 'There is no merge proposal at %s' % email_addr, mail) return True except BadBranchMergeProposalAddress: return False context = CodeReviewEmailCommandExecutionContext(merge_proposal, user) try: email_body_text = get_main_body(mail) commands = CodeEmailCommands.getCommands(email_body_text) processed_count = self.processCommands(context, commands) # Make sure that the email is in fact signed. if processed_count > 0: ensure_not_weakly_authenticated(mail, 'code review') message = getUtility(IMessageSet).fromEmail( mail.parsed_string, owner=getUtility(ILaunchBag).user, filealias=file_alias, parsed_message=mail) merge_proposal.createCommentFromMessage( message, context.vote, context.vote_tags, mail) except IncomingEmailError as error: send_process_error_notification( str(user.preferredemail.email), 'Submit Request Failure', error.message, mail, error.failing_command) transaction.abort() return True
def processComment(self, mail, email_addr, file_alias): """Process an email and create a CodeReviewComment. The only mail command understood is 'vote', which takes 'approve', 'disapprove', or 'abstain' as values. Specifically, it takes any CodeReviewVote item value, case-insensitively. :return: True. """ user = getUtility(ILaunchBag).user try: merge_proposal = self.getBranchMergeProposal(email_addr) except NonExistantBranchMergeProposalAddress: send_process_error_notification( str(user.preferredemail.email), 'Submit Request Failure', 'There is no merge proposal at %s' % email_addr, mail) return True except BadBranchMergeProposalAddress: return False context = CodeReviewEmailCommandExecutionContext(merge_proposal, user) try: email_body_text = get_main_body(mail) commands = CodeEmailCommands.getCommands(email_body_text) processed_count = self.processCommands(context, commands) # Make sure that the email is in fact signed. if processed_count > 0: ensure_not_weakly_authenticated(mail, 'code review') message = getUtility(IMessageSet).fromEmail( mail.parsed_string, owner=getUtility(ILaunchBag).user, filealias=file_alias, parsed_message=mail) merge_proposal.createCommentFromMessage(message, context.vote, context.vote_tags, mail) except IncomingEmailError as error: send_process_error_notification(str(user.preferredemail.email), 'Submit Request Failure', error.message, mail, error.failing_command) transaction.abort() return True
def _send_email_oops(trans, log, mail, error_msg, file_alias_url): """Handle an error that generates an oops. It does the following: * records an OOPS with error_msg and file_alias_url * commits the current transaction to ensure that the message gets sent """ log.info('error processing mail: %s' % (error_msg,)) oops_id = report_oops( file_alias_url=file_alias_url, error_msg=error_msg) log.info('oops %s' % (oops_id,)) send_process_error_notification( mail['From'], 'Submit Request Failure', get_error_message('oops.txt', oops_id=oops_id), mail) trans.commit()
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 process(self, signed_msg, to_addr, filealias=None, log=None): """See IMailHandler.""" try: ( final_result, add_comment_to_bug, commands, ) = self.extractAndAuthenticateCommands(signed_msg, to_addr) if final_result is not None: return final_result bug = None bug_event = None bugtask = None bugtask_event = None processing_errors = [] while len(commands) > 0: command = commands.pop(0) try: if IBugEmailCommand.providedBy(command): # Finish outstanding work from the previous bug. self.notify_bug_event(bug_event) self.notify_bugtask_event(bugtask_event, bug_event) bugtask = None bugtask_event = None # Get or start building a new bug. bug, bug_event = command.execute(signed_msg, filealias) if add_comment_to_bug: message = self.appendBugComment( bug, signed_msg, filealias) add_comment_to_bug = False self.processAttachments(bug, message, signed_msg) elif IBugTaskEmailCommand.providedBy(command): self.notify_bugtask_event(bugtask_event, bug_event) bugtask, bugtask_event, bug_event = command.execute( bug, bug_event) if isinstance(bug, CreateBugParams): bug = bugtask.bug message = bug.initial_message self.processAttachments(bug, message, signed_msg) elif IBugEditEmailCommand.providedBy(command): bug, bug_event = command.execute(bug, bug_event) elif IBugTaskEditEmailCommand.providedBy(command): if bugtask is None: if isinstance(bug, CreateBugParams): self.handleNoAffectsTarget() bugtask = guess_bugtask( bug, getUtility(ILaunchBag).user) if bugtask is None: self.handleNoDefaultAffectsTarget(bug) bugtask, bugtask_event = command.execute( bugtask, bugtask_event) except EmailProcessingError as error: processing_errors.append((error, command)) if error.stop_processing: commands = [] transaction.abort() else: continue if len(processing_errors) > 0: raise IncomingEmailError( '\n'.join( str(error) for error, command in processing_errors), [command for error, command in processing_errors]) if isinstance(bug, CreateBugParams): # A new bug without any commands was sent. self.handleNoAffectsTarget() self.notify_bug_event(bug_event) self.notify_bugtask_event(bugtask_event, bug_event) except IncomingEmailError as error: send_process_error_notification( str(getUtility(ILaunchBag).user.preferredemail.email), 'Submit Request Failure', error.message, signed_msg, error.failing_command) return True
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 process(self, signed_msg, to_addr, filealias=None, log=None): """See IMailHandler.""" try: (final_result, add_comment_to_bug, commands, ) = self.extractAndAuthenticateCommands( signed_msg, to_addr) if final_result is not None: return final_result bug = None bug_event = None bugtask = None bugtask_event = None processing_errors = [] while len(commands) > 0: command = commands.pop(0) try: if IBugEmailCommand.providedBy(command): # Finish outstanding work from the previous bug. self.notify_bug_event(bug_event) self.notify_bugtask_event(bugtask_event, bug_event) bugtask = None bugtask_event = None # Get or start building a new bug. bug, bug_event = command.execute(signed_msg, filealias) if add_comment_to_bug: message = self.appendBugComment( bug, signed_msg, filealias) add_comment_to_bug = False self.processAttachments(bug, message, signed_msg) elif IBugTaskEmailCommand.providedBy(command): self.notify_bugtask_event(bugtask_event, bug_event) bugtask, bugtask_event, bug_event = command.execute( bug, bug_event) if isinstance(bug, CreateBugParams): bug = bugtask.bug message = bug.initial_message self.processAttachments(bug, message, signed_msg) elif IBugEditEmailCommand.providedBy(command): bug, bug_event = command.execute(bug, bug_event) elif IBugTaskEditEmailCommand.providedBy(command): if bugtask is None: if isinstance(bug, CreateBugParams): self.handleNoAffectsTarget() bugtask = guess_bugtask( bug, getUtility(ILaunchBag).user) if bugtask is None: self.handleNoDefaultAffectsTarget(bug) bugtask, bugtask_event = command.execute( bugtask, bugtask_event) except EmailProcessingError as error: processing_errors.append((error, command)) if error.stop_processing: commands = [] transaction.abort() else: continue if len(processing_errors) > 0: raise IncomingEmailError( '\n'.join(str(error) for error, command in processing_errors), [command for error, command in processing_errors]) if isinstance(bug, CreateBugParams): # A new bug without any commands was sent. self.handleNoAffectsTarget() self.notify_bug_event(bug_event) self.notify_bugtask_event(bugtask_event, bug_event) except IncomingEmailError as error: send_process_error_notification( str(getUtility(ILaunchBag).user.preferredemail.email), 'Submit Request Failure', error.message, signed_msg, error.failing_command) return True