Example #1
0
    def testProcessMail_NoProjectOnToLine(self):
        self.mox.StubOutWithMock(emailfmt, 'IsProjectAddressOnToLine')
        emailfmt.IsProjectAddressOnToLine(self.project_addr,
                                          [self.project_addr]).AndReturn(False)
        self.mox.ReplayAll()

        ret = self.inbound.ProcessMail(self.msg, self.project_addr)
        self.mox.VerifyAll()
        self.assertIsNone(ret)
Example #2
0
    def testProcessMail_Success_with_AlertNotification(self):
        """Test ProcessMail with an alert notification message.

    This is a sanity check for alert2issue.ProcessEmailNotification to ensure
    that it can be successfully invoked in ProcessMail. Each function of
    alert2issue module should be tested in aler2issue_test.
    """
        project_name = self.project.project_name
        verb = 'alert'
        trooper_queue = 'my-trooper'
        project_addr = '*****@*****.**' % (project_name, verb,
                                                 trooper_queue)

        self.mox.StubOutWithMock(emailfmt, 'IsProjectAddressOnToLine')
        emailfmt.IsProjectAddressOnToLine(project_addr,
                                          mox.IgnoreArg()).AndReturn(True)

        class MockAuthData(object):
            def __init__(self):
                self.user_pb = user_pb2.MakeUser(111)
                self.effective_ids = set([1, 2, 3])
                self.user_id = 111
                self.email = '*****@*****.**'

        mock_auth_data = MockAuthData()
        self.mox.StubOutWithMock(authdata.AuthData, 'FromEmail')
        authdata.AuthData.FromEmail(mox.IgnoreArg(),
                                    settings.alert_service_account,
                                    self.services,
                                    autocreate=True).AndReturn(mock_auth_data)

        self.mox.StubOutWithMock(alert2issue, 'ProcessEmailNotification')
        alert2issue.ProcessEmailNotification(self.services, mox.IgnoreArg(),
                                             self.project, project_addr,
                                             mox.IgnoreArg(), mock_auth_data,
                                             mox.IgnoreArg(), 'awesome!', '',
                                             self.msg, trooper_queue)

        self.mox.ReplayAll()
        ret = self.inbound.ProcessMail(self.msg, project_addr)
        self.mox.VerifyAll()
        self.assertIsNone(ret)
Example #3
0
  def ProcessMail(self, msg, project_addr):
    """Process an inbound email message."""
    # TODO(jrobbins): If the message is HUGE, don't even try to parse
    # it. Silently give up.

    (from_addr, to_addrs, cc_addrs, references, incident_id, subject,
     body) = emailfmt.ParseEmailMessage(msg)

    logging.info('Proj addr:   %r', project_addr)
    logging.info('From addr:   %r', from_addr)
    logging.info('Subject:     %r', subject)
    logging.info('To:          %r', to_addrs)
    logging.info('Cc:          %r', cc_addrs)
    logging.info('References:  %r', references)
    logging.info('Incident Id: %r', incident_id)
    logging.info('Body:        %r', body)

    # If message body is very large, reject it and send an error email.
    if emailfmt.IsBodyTooBigToParse(body):
      return _MakeErrorMessageReplyTask(
          project_addr, from_addr, self._templates['body_too_long'])

    # Make sure that the project reply-to address is in the To: line.
    if not emailfmt.IsProjectAddressOnToLine(project_addr, to_addrs):
      return None

    project_name, verb, trooper_queue = emailfmt.IdentifyProjectVerbAndLabel(
        project_addr)

    is_alert = bool(verb and verb.lower() == 'alert')
    error_addr = from_addr
    local_id = None
    author_addr = from_addr

    if is_alert:
      error_addr = settings.alert_escalation_email
      author_addr = settings.alert_service_account
    else:
      local_id = emailfmt.IdentifyIssue(project_name, subject)
      if not local_id:
        logging.info('Could not identify issue: %s %s', project_addr, subject)
        # No error message, because message was probably not intended for us.
        return None

    cnxn = sql.MonorailConnection()
    if self.services.cache_manager:
      self.services.cache_manager.DoDistributedInvalidation(cnxn)

    project = self.services.project.GetProjectByName(cnxn, project_name)
    # Authenticate the author_addr and perm check.
    try:
      mc = monorailcontext.MonorailContext(
          self.services, cnxn=cnxn, requester=author_addr, autocreate=is_alert)
      mc.LookupLoggedInUserPerms(project)
    except exceptions.NoSuchUserException:
      return _MakeErrorMessageReplyTask(
          project_addr, error_addr, self._templates['no_account'])

    # TODO(zhangtiff): Add separate email templates for alert error cases.
    if not project or project.state != project_pb2.ProjectState.LIVE:
      return _MakeErrorMessageReplyTask(
          project_addr, error_addr, self._templates['project_not_found'])

    if not project.process_inbound_email:
      return _MakeErrorMessageReplyTask(
          project_addr, error_addr, self._templates['replies_disabled'],
          project_name=project_name)

    # Verify that this is a reply to a notification that we could have sent.
    is_development = os.environ['SERVER_SOFTWARE'].startswith('Development')
    if not (is_alert or is_development):
      for ref in references:
        if emailfmt.ValidateReferencesHeader(ref, project, from_addr, subject):
          break  # Found a message ID that we could have sent.
        if emailfmt.ValidateReferencesHeader(
            ref, project, from_addr.lower(), subject):
          break  # Also match all-lowercase from-address.
      else: # for-else: if loop completes with no valid reference found.
        return _MakeErrorMessageReplyTask(
            project_addr, from_addr, self._templates['not_a_reply'])

    # Note: If the issue summary line is changed, a new thread is created,
    # and replies to the old thread will no longer work because the subject
    # line hash will not match, which seems reasonable.

    if mc.auth.user_pb.banned:
      logging.info('Banned user %s tried to post to %s',
                   from_addr, project_addr)
      return _MakeErrorMessageReplyTask(
          project_addr, error_addr, self._templates['banned'])

    # If the email is an alert, switch to the alert handling path.
    if is_alert:
      alert2issue.ProcessEmailNotification(
          self.services, cnxn, project, project_addr, from_addr,
          mc.auth, subject, body, incident_id, msg, trooper_queue)
      return None

    # This email is a response to an email about a comment.
    self.ProcessIssueReply(
        mc, project, local_id, project_addr, body)

    return None