/server/src/main/java/org/eurekastreams/server/service/email/MessageProcessor.java
http://github.com/lmco/eurekastreams · Java · 337 lines · 181 code · 30 blank · 126 comment · 24 complexity · 5f6e95f85708916c9120089359da6491 MD5 · raw file
- /*
- * Copyright (c) 2011 Lockheed Martin Corporation
- *
- * 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 org.eurekastreams.server.service.email;
- import java.io.IOException;
- import java.util.List;
- import java.util.Map;
- import javax.mail.Address;
- import javax.mail.Message;
- import javax.mail.Message.RecipientType;
- import javax.mail.MessagingException;
- import javax.mail.internet.InternetAddress;
- import org.eurekastreams.commons.actions.context.DefaultPrincipal;
- import org.eurekastreams.commons.actions.context.PrincipalActionContext;
- import org.eurekastreams.commons.actions.context.service.ServiceActionContext;
- import org.eurekastreams.commons.actions.service.ServiceAction;
- import org.eurekastreams.commons.actions.service.TaskHandlerServiceAction;
- import org.eurekastreams.commons.exceptions.ExecutionException;
- import org.eurekastreams.commons.exceptions.ValidationException;
- import org.eurekastreams.commons.server.UserActionRequest;
- import org.eurekastreams.commons.server.service.ActionController;
- import org.eurekastreams.server.persistence.mappers.DomainMapper;
- import org.eurekastreams.server.search.modelview.PersonModelView;
- import org.springframework.beans.factory.BeanFactory;
- import org.springframework.transaction.PlatformTransactionManager;
- import org.springframework.transaction.TransactionStatus;
- import org.springframework.transaction.support.DefaultTransactionDefinition;
- /**
- * Responsible for all processing of a single received email message.
- */
- public class MessageProcessor
- {
- /** For getting the user's content from the message. */
- private final MessageContentExtractor messageContentExtractor;
- /** Determines which action to execute. */
- private final ActionSelectorFactory actionSelector;
- /** Instance of {@link ActionController} used to run actions. */
- private final ActionController serviceActionController;
- /** The context from which this service can load action beans. */
- private final BeanFactory beanFactory;
- /** For decoding the token. */
- private final TokenEncoder tokenEncoder;
- /** Parses the token content. */
- private final TokenContentFormatter tokenContentFormatter;
- /** Transaction manager (to allow calling mappers). */
- private final PlatformTransactionManager transactionMgr;
- /** DAO to get a user's person ID given their email address. */
- private final DomainMapper<String, Long> personIdByEmailDao;
- /** DAO to get a user's crypto key given their person ID. */
- private final DomainMapper<Long, byte[]> userKeyByIdDao;
- /** DAO to get a person by ID. */
- private final DomainMapper<Long, PersonModelView> personDao;
- /** Responds to messages that failed execution with result status. */
- private final MessageReplier messageReplier;
- /** Address messages must be sent to. */
- private final String requiredToAddress;
- /** Text address must begin with to be the desired To address. */
- private final String toEmailRequiredStart;
- /** Text address must end with to be the desired To address. */
- private final String toEmailRequiredEnd;
- /**
- * Constructor.
- *
- * @param inMessageContentExtractor
- * For getting the user's content from the message.
- * @param inActionSelector
- * Determines which action to execute.
- * @param inServiceActionController
- * Instance of {@link ActionController} used to run actions.
- * @param inBeanFactory
- * The context from which this service can load action beans.
- * @param inTokenEncoder
- * For decoding the token.
- * @param inTokenContentFormatter
- * Parses the token content.
- * @param inTransactionMgr
- * Transaction manager (to allow calling mappers).
- * @param inPersonIdByEmailDao
- * DAO to get a user's person ID given their email address.
- * @param inUserKeyByIdDao
- * DAO to get a user's crypto key given their person ID.
- * @param inPersonDao
- * DAO to get a person by ID.
- * @param inMessageReplier
- * Responds to messages that failed execution with result status.
- * @param inRequiredToAddress
- * Address messages must be sent to.
- */
- public MessageProcessor(final MessageContentExtractor inMessageContentExtractor,
- final ActionSelectorFactory inActionSelector, final ActionController inServiceActionController,
- final BeanFactory inBeanFactory, final TokenEncoder inTokenEncoder,
- final TokenContentFormatter inTokenContentFormatter, final PlatformTransactionManager inTransactionMgr,
- final DomainMapper<String, Long> inPersonIdByEmailDao, final DomainMapper<Long, byte[]> inUserKeyByIdDao,
- final DomainMapper<Long, PersonModelView> inPersonDao, final MessageReplier inMessageReplier,
- final String inRequiredToAddress)
- {
- messageContentExtractor = inMessageContentExtractor;
- actionSelector = inActionSelector;
- serviceActionController = inServiceActionController;
- beanFactory = inBeanFactory;
- tokenEncoder = inTokenEncoder;
- tokenContentFormatter = inTokenContentFormatter;
- transactionMgr = inTransactionMgr;
- personIdByEmailDao = inPersonIdByEmailDao;
- userKeyByIdDao = inUserKeyByIdDao;
- personDao = inPersonDao;
- messageReplier = inMessageReplier;
- requiredToAddress = inRequiredToAddress;
- int pos = inRequiredToAddress.indexOf('@');
- toEmailRequiredStart = inRequiredToAddress.substring(0, pos) + "+";
- toEmailRequiredEnd = inRequiredToAddress.substring(pos);
- }
- /**
- * Processes the message.
- *
- * @param message
- * Message to process.
- * @param inResponseMessages
- * List to add response messages to.
- * @return If message was processed.
- * @throws MessagingException
- * On error.
- * @throws IOException
- * On error.
- */
- public boolean execute(final Message message, final List<Message> inResponseMessages) throws MessagingException,
- IOException
- {
- String token = getToken(message);
- if (token == null)
- {
- return false;
- }
- String fromAddress = getFromAddress(message);
- // get the sender and sender's key
- DefaultTransactionDefinition transDef = new DefaultTransactionDefinition();
- transDef.setName("TokenAddressMessageAuthenticator");
- transDef.setReadOnly(false);
- TransactionStatus transStatus = transactionMgr.getTransaction(transDef);
- byte[] key;
- Long personId;
- PersonModelView person;
- try
- {
- personId = personIdByEmailDao.execute(fromAddress);
- key = userKeyByIdDao.execute(personId);
- person = personDao.execute(personId);
- }
- finally
- {
- transactionMgr.commit(transStatus);
- }
- // decrypt and unpack the token
- Map<String, Long> tokenData = getTokenData(token, key);
- // get the message content
- String content = messageContentExtractor.extract(message);
- // choose action to execute
- UserActionRequest actionSelection = actionSelector.select(tokenData, content, person);
- // execute action
- executeAction(message, actionSelection, person, inResponseMessages);
- return true;
- }
- /**
- * Decrypts and unpacks the token.
- *
- * @param token
- * Raw token.
- * @param key
- * User's key.
- * @return Data contained in token.
- */
- Map<String, Long> getTokenData(final String token, final byte[] key)
- {
- String tokenConent = tokenEncoder.decode(token, key);
- if (tokenConent == null)
- {
- throw new ValidationException("Cannot decrypt token for user.");
- }
- Map<String, Long> tokenData = tokenContentFormatter.parse(tokenConent);
- if (tokenData == null)
- {
- throw new ValidationException("Cannot parse token.");
- }
- return tokenData;
- }
- /**
- * Execute the selected action.
- *
- * @param message
- * The original message.
- * @param actionSelection
- * The selected action.
- * @param person
- * The user.
- * @param inResponseMessages
- * List to add response messages to.
- */
- void executeAction(final Message message, final UserActionRequest actionSelection, final PersonModelView person,
- final List<Message> inResponseMessages)
- {
- try
- {
- Object springBean = beanFactory.getBean(actionSelection.getActionKey());
- PrincipalActionContext actionContext = new ServiceActionContext(actionSelection.getParams(),
- new DefaultPrincipal(person.getAccountId(), person.getOpenSocialId(), person.getId()));
- actionContext.setActionId(actionSelection.getActionKey());
- if (springBean instanceof ServiceAction)
- {
- ServiceAction action = (ServiceAction) springBean;
- serviceActionController.execute(actionContext, action);
- }
- else if (springBean instanceof TaskHandlerServiceAction)
- {
- TaskHandlerServiceAction action = (TaskHandlerServiceAction) springBean;
- serviceActionController.execute(actionContext, action);
- }
- else
- {
- throw new ExecutionException("Bean '" + actionSelection.getActionKey()
- + "' is not an executable action");
- }
- }
- catch (RuntimeException ex)
- {
- // notify user on failure
- // Note: A response is only sent for errors processing the action (which could be due to missing content
- // from the message). This is because any errors encountered prior represent a bad sender or ill-formed
- // message or token and thus represent a suspicious message. In that case we don't want to send a reply, for
- // security.
- messageReplier.reply(message, person, actionSelection, ex, inResponseMessages);
- throw ex;
- }
- }
- /**
- * Extracts the FROM address from the message.
- *
- * @param message
- * The message.
- * @return The FROM address.
- * @throws MessagingException
- * On error.
- */
- String getFromAddress(final Message message) throws MessagingException
- {
- // insure the message has a From address
- Address[] addresses = message.getFrom();
- if (addresses == null || addresses.length != 1 || addresses[0] == null)
- {
- throw new ValidationException("Message must contain a single From address.");
- }
- return ((InternetAddress) addresses[0]).getAddress();
- }
- /**
- * Extracts the token from the message.
- *
- * @param message
- * The message.
- * @return The token.
- * @throws MessagingException
- * On error.
- */
- String getToken(final Message message) throws MessagingException
- {
- // insure the message has a To address which 1) matches the expected system address, and 2) has an address tag
- Address[] addresses = message.getRecipients(RecipientType.TO);
- if (addresses != null)
- {
- boolean noReplyFound = false;
- for (int i = 0; i < addresses.length; i++)
- {
- String addr = ((InternetAddress) addresses[i]).getAddress();
- // check for token-less system address: no-reply
- if (requiredToAddress.equals(addr))
- {
- noReplyFound = true;
- }
- // check for token
- else if (addr.startsWith(toEmailRequiredStart) && addr.endsWith(toEmailRequiredEnd))
- {
- String middle = addr.substring(toEmailRequiredStart.length(),
- addr.length() - toEmailRequiredEnd.length());
- if (tokenEncoder.couldBeToken(middle))
- {
- return middle;
- }
- }
- }
- if (noReplyFound)
- {
- return null;
- }
- }
- throw new ValidationException("Cannot find To address for the system with an address tag.");
- }
- }