Package cave.nice.testMessage.mail

Source Code of cave.nice.testMessage.mail.MailHandlerServlet

/*
* Copyright 2011 Michele Mancioppi [michele.mancioppi@gmail.com]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cave.nice.testMessage.mail;

import java.io.IOException;
import java.util.Date;
import java.util.Properties;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMessage.RecipientType;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;

import cave.nice.testMessage.TestMessageConstants;
import cave.nice.testMessage.data.CannotUpdateEntityException;
import cave.nice.testMessage.data.DataManager;
import cave.nice.testMessage.data.DataManagerException;
import cave.nice.testMessage.data.Test;
import cave.nice.testMessage.data.TestAlreadyAnsweredException;
import cave.nice.testMessage.data.UknownTestChallengeException;
import cave.nice.testMessage.data.UnknownVerifiedAccountException;
import cave.nice.testMessage.data.UnprocessedEMail;
import cave.nice.testMessage.data.VerifiedAccount;
import cave.nice.testMessage.data.WrongAccountForTestException;

import com.google.appengine.api.datastore.KeyFactory;

/**
* Processes the incoming messages. They are supposed to be test answers. If
* not, make a fuss.
*
* @author Michele
*/
@SuppressWarnings("serial")
public class MailHandlerServlet extends HttpServlet {

  private static final Logger LOGGER = Logger
      .getLogger(MailHandlerServlet.class.getName());

  private InternetAddress appInternetAddress = null;

  @Override
  public void init(ServletConfig config) throws ServletException {
    try {
      appInternetAddress = new InternetAddress(
          TestMessageConstants.TESTS_EMAIL_ADDRESS, true);
    } catch (AddressException e) {
      throw new ServletException(
          "Error while creating the app internet address using the email '"
              + TestMessageConstants.TESTS_EMAIL_ADDRESS + "'", e);
    }
  }

  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    DataManager dataStoreManager = DataManager.getInstance();

    try {
      Properties props = new Properties();
      Session session = Session.getDefaultInstance(props, null);
      MimeMessage message = null;
      try {
        message = new MimeMessage(session, req.getInputStream());
      } catch (MessagingException e) {
        throw new ServletException(
            "Error while retrieving an incoming mail", e);
      }
      Address[] froms = null;
      try {
        froms = message.getFrom();
      } catch (MessagingException e) {
        throw new ServletException(
            "Error while retrieving an the 'from' field from the following message: "
                + message, e);
      }
      InternetAddress from = null;
      switch (froms.length) {
      case 0: {
        /*
         * No from address? How are we supposed to check the validity of
         * the test?
         *
         * TODO Check if the email has at least a valid test identifier
         * inside. If not, figure out something
         */
        throw new ServletException(
            "The following message has no 'from' field set: "
                + message);
      }
      case 1: {
        /*
         * That's how we like it ;-)
         */
        from = (InternetAddress) froms[0];
        break;
      }
      default: {
        /*
         * Uhm... multiple 'from'? TODO Check for a 'from' we know, and
         * assume that. Plus, log a warning.
         */
        throw new ServletException(
            "The following message has multiple 'from' fields set: "
                + message);
      }

      }
      try {
        if (message
            .getHeader(TestMessageConstants.SMTP_HEADER_TEST_IDENTIFIER) != null
            || message
                .getHeader(TestMessageConstants.SMTP_HEADER_ACCOUNT_IDENTIFIER) != null) {
          processTestAnswerInHeaders(dataStoreManager, message);
        } else {
          processTestAnswerInMessageBody(dataStoreManager, message,
              from);
        }
      } catch (DataManagerException e) {
        throw new ServletException(e.getMessage());
      } catch (Exception e) {
        UnprocessedEMail em = dataStoreManager
            .storeUnprocessedEMail(message);
        LOGGER.log(Level.SEVERE,
            "Incoming message could not be processed, saved as UnprocessedEMail "
                + KeyFactory.keyToString(em.getIdentifier()), e);
      }
    } finally {
      dataStoreManager.close();
    }
  }

  private void processTestAnswerInHeaders(DataManager dataManager,
      MimeMessage message) throws ServletException,
      UknownTestChallengeException, UnknownVerifiedAccountException,
      WrongAccountForTestException, CannotUpdateEntityException {
    try {
      String[] testIdentifiers = message
          .getHeader(TestMessageConstants.SMTP_HEADER_TEST_IDENTIFIER);
      if (testIdentifiers.length == 0) {
        throw new ServletException("Required SMTP header '"
            + TestMessageConstants.SMTP_HEADER_TEST_IDENTIFIER
            + "' not found");
      } else if (testIdentifiers.length > 1) {
        throw new ServletException(
            "More than one value for the header '"
                + TestMessageConstants.SMTP_HEADER_TEST_IDENTIFIER
                + "' found");
      }

      String[] accountIdentifiers = message
          .getHeader(TestMessageConstants.SMTP_HEADER_ACCOUNT_IDENTIFIER);
      if (accountIdentifiers.length == 0) {
        throw new ServletException("Required SMTP header '"
            + TestMessageConstants.SMTP_HEADER_ACCOUNT_IDENTIFIER
            + "' not found");
      } else if (accountIdentifiers.length > 1) {
        throw new ServletException(
            "More than one value for the header '"
                + TestMessageConstants.SMTP_HEADER_ACCOUNT_IDENTIFIER
                + "' found");
      }

      String testChallenge = testIdentifiers[0];
      UUID challenge = UUID.fromString(testChallenge);
      String accountIdentifier = accountIdentifiers[0];

      VerifiedAccount account = dataManager.getVerifiedAccount(KeyFactory
          .stringToKey(accountIdentifier));

      processSMTPTestAnswer(dataManager, account, challenge);
    } catch (MessagingException e) {
      throw new ServletException(
          "Error while accessing the headers of the message", e);
    } catch (TestAlreadyAnsweredException e) {
      /*
       * TODO Error message
       */
      throw new ServletException(e);
    }

  }

  private void processTestAnswerInMessageBody(DataManager dataStoreManager,
      MimeMessage message, InternetAddress from) throws ServletException,
      IOException, UnknownVerifiedAccountException,
      WrongAccountForTestException, CannotUpdateEntityException,
      UknownTestChallengeException, TestAlreadyAnsweredException {
    /*
     * It could be that the address is the one used to send the test in the
     * first place :D
     */
    VerifiedAccount account = null;
    if (from.equals(appInternetAddress)) {
      /*
       * OK, they just forwarded it.
       */
      Address[] tos = null;
      try {
        tos = message.getRecipients(RecipientType.TO);
      } catch (MessagingException e) {
        throw new ServletException(
            "Cannot access TO's of the message: " + message);
      }

      if (tos != null && tos.length > 0) {
        for (Address to : tos) {
          if (!(to instanceof InternetAddress)) {
            /*
             * Well, if this is the case, we want nothing to do with
             * this address :D
             */
            continue;
          }

          try {
            account = dataStoreManager
                .getVerifiedAccount((InternetAddress) to);
            break;
          } catch (UnknownVerifiedAccountException e) {
            /*
             * We don't know this. Strange, but let's move on
             */
          }
        }
      }

      if (account == null) {
        throw new ServletException("Message to unknown accounts '"
            + tos + "': " + message);
      }
    } else {
      /*
       * It is not a forward... Could it be a reply?
       */
      try {
        account = dataStoreManager.getVerifiedAccount(from);
      } catch (UnknownVerifiedAccountException e) {
        throw new ServletException("Message from unknown account '"
            + from + "': " + message);
      }
    }

    /*
     * OK, we know the account. Now, do we recognize the test that is
     * answered?
     */
    String body = null;
    try {
      body = IOUtils.toString(message.getInputStream());
      body = body.replaceAll("\\s", "");
    } catch (MessagingException e) {
      throw new ServletException("Cannot access content of the message:"
          + message);
    }

    /*
     * Remove from body the white spaces: if one has been inserted in the
     * id, we would not recognize it
     */
    Matcher matcher = null;
    String pattern = ".*#####(.+)#####.*";
    try {
      matcher = Pattern.compile(pattern).matcher(body);
    } catch (PatternSyntaxException e) {
      throw new ServletException("Cannot compile pattern: " + pattern, e);
    }

    if (!matcher.matches()) {
      throw new ServletException("No test identifier found in the body: "
          + body);
    }

    String testChallenge = matcher.group(1);
    UUID challenge = UUID.fromString(testChallenge);

    processSMTPTestAnswer(dataStoreManager, account, challenge);
  }

  private void processSMTPTestAnswer(DataManager dataStoreManager,
      VerifiedAccount account, UUID testChallenge)
      throws UknownTestChallengeException,
      UnknownVerifiedAccountException, WrongAccountForTestException,
      CannotUpdateEntityException, TestAlreadyAnsweredException {
    Test openTest = dataStoreManager.getTestOfAccount(account,
        testChallenge);

    LOGGER.info("SMTP answer to the test '"
        + KeyFactory.keyToString(openTest.getIdentifier())
        + "' of the email account '"
        + openTest.getAccount().getEmailAddress() + "'");

    dataStoreManager.markTestAsAnswered(openTest, new Date());
  }

}
TOP

Related Classes of cave.nice.testMessage.mail.MailHandlerServlet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.