示例#1
0
 def CheckIdentifiedValues(self,
                           project_addr,
                           subject,
                           expected_project_name,
                           expected_local_id,
                           expected_verb=None):
     """Testing helper function to check 3 results against expected values."""
     project_name, verb = emailfmt.IdentifyProjectAndVerb(project_addr)
     local_id = emailfmt.IdentifyIssue(project_name, subject)
     self.assertEqual(expected_project_name, project_name)
     self.assertEqual(expected_local_id, local_id)
     self.assertEqual(expected_verb, verb)
示例#2
0
    def testProcessMail_IssueUnidentified(self):
        self.mox.StubOutWithMock(emailfmt, 'IdentifyProjectAndVerb')
        emailfmt.IdentifyProjectAndVerb(self.project_addr).AndReturn(
            ('proj', None))

        self.mox.StubOutWithMock(emailfmt, 'IdentifyIssue')
        emailfmt.IdentifyIssue('proj', mox.IgnoreArg()).AndReturn((None))

        self.mox.ReplayAll()

        ret = self.inbound.ProcessMail(self.msg, self.project_addr)
        self.mox.VerifyAll()
        self.assertIsNone(ret)
示例#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