org.apache.hupa.server.service.SendMessageBaseServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hupa.server.service.SendMessageBaseServiceImpl.java

Source

/****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one   *
 * or more contributor license agreements.  See the NOTICE file *
 * distributed with this work for additional information        *
 * regarding copyright ownership.  The ASF licenses this file   *
 * to you 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.apache.hupa.server.service;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.activation.DataSource;
import javax.mail.AuthenticationFailedException;
import javax.mail.BodyPart;
import javax.mail.Flags.Flag;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMessage.RecipientType;
import javax.mail.internet.MimeMultipart;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.hupa.server.FileItemRegistry;
import org.apache.hupa.server.IMAPStoreCache;
import org.apache.hupa.server.preferences.UserPreferencesStorage;
import org.apache.hupa.server.utils.MessageUtils;
import org.apache.hupa.server.utils.RegexPatterns;
import org.apache.hupa.server.utils.SessionUtils;
import org.apache.hupa.shared.SConsts;
import org.apache.hupa.shared.data.GenericResultImpl;
import org.apache.hupa.shared.domain.GenericResult;
import org.apache.hupa.shared.domain.MessageAttachment;
import org.apache.hupa.shared.domain.SendMessageAction;
import org.apache.hupa.shared.domain.SmtpMessage;
import org.apache.hupa.shared.domain.User;
import org.apache.hupa.shared.exception.HupaException;

import com.google.inject.Inject;
import com.google.web.bindery.requestfactory.server.RequestFactoryServlet;
import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.imap.IMAPStore;

public class SendMessageBaseServiceImpl extends AbstractService implements SendMessageService {

    UserPreferencesStorage userPreferences;

    @Inject
    public SendMessageBaseServiceImpl(UserPreferencesStorage preferences, IMAPStoreCache cache) {
        this.cache = cache;
        this.userPreferences = preferences;
    }

    public GenericResult send(SendMessageAction action) throws Exception {
        GenericResult result = new GenericResultImpl();
        try {
            User user = getUser();
            Message message = createMessage(cache.getMailSession(user), action);
            message = fillBody(message, action);
            sendMessage(getUser(), message);
            if (!user.getSettings().getSmtpServer().contains("gmail.com")) {
                saveSentMessage(getUser(), message);
            }
            resetAttachments(action);

            // TODO: notify the user more accurately where the error is
            // if the message was sent and the storage in the sent folder failed, etc.
        } catch (AddressException e) {
            result.setError("Error while parsing recipient: " + e.getMessage());
            logger.error("Error while parsing recipient", e);
        } catch (AuthenticationFailedException e) {
            result.setError("Error while sending message: SMTP Authentication error.");
            logger.error("SMTP Authentication error", e);
        } catch (MessagingException e) {
            result.setError("Error while sending message: " + e.getMessage());
            logger.error("Error while sending message", e);
        } catch (Exception e) {
            result.setError("Unexpected exception while sendig message: " + e.getMessage());
            logger.error("Unexpected exception while sendig message: ", e);
        }
        return result;
    }

    /**
     * Create basic Message which contains all headers. No body is filled
     *
     * @param session the Session
     * @param action the action
     * @return message
     * @throws AddressException
     * @throws MessagingException
     */
    public Message createMessage(Session session, SendMessageAction action)
            throws AddressException, MessagingException {
        MimeMessage message = new MimeMessage(session);
        SmtpMessage m = action.getMessage();
        message.setFrom(new InternetAddress(m.getFrom()));

        userPreferences.addContact(m.getTo());
        userPreferences.addContact(m.getCc());
        userPreferences.addContact(m.getBcc());

        message.setRecipients(RecipientType.TO, MessageUtils.getRecipients(m.getTo()));
        message.setRecipients(RecipientType.CC, MessageUtils.getRecipients(m.getCc()));
        message.setRecipients(RecipientType.BCC, MessageUtils.getRecipients(m.getBcc()));
        message.setSentDate(new Date());
        message.addHeader("User-Agent:", "HUPA, The Apache JAMES webmail client.");
        message.addHeader("X-Originating-IP", getClientIpAddr());
        message.setSubject(m.getSubject(), "utf-8");
        updateHeaders(message, action);
        message.saveChanges();
        return message;
    }

    public static String getClientIpAddr() {
        HttpServletRequest request = RequestFactoryServlet.getThreadLocalRequest();
        String ip = "unknown";
        if (request != null) {
            ip = request.getHeader("X-Forwarded-For");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        }
        return ip;
    }

    protected void updateHeaders(MimeMessage message, SendMessageAction action) {
        if (action.getInReplyTo() != null) {
            try {
                message.addHeader(SConsts.HEADER_IN_REPLY_TO, action.getInReplyTo());
            } catch (MessagingException e) {
                logger.error("Error while setting header:" + e.getMessage(), e);
            }
        }
        if (action.getReferences() != null) {
            try {
                message.addHeader(SConsts.HEADER_REFERENCES, action.getReferences());
            } catch (MessagingException e) {
                logger.error("Error while setting header:" + e.getMessage(), e);
            }
        }
    }

    /**
     * Fill the body of the given message with data which the given action contain
     *
     * @param message the message
     * @param action the action
     * @return filledMessage
     * @throws MessagingException
     * @throws IOException
     * @throws HupaException
     */
    public Message fillBody(Message message, SendMessageAction action)
            throws MessagingException, IOException, HupaException {

        String html = restoreInlineLinks(action.getMessage().getText());

        // TODO: client sends the message as a html document right now,
        // the idea is that it should be sent in both formats because
        // it is easier to handle html in the browser.
        String text = htmlToText(html);

        @SuppressWarnings("rawtypes")
        List items = getAttachments(action);

        return composeMessage(message, text, html, items);
    }

    protected String restoreInlineLinks(String s) {
        return RegexPatterns.replaceAll(s, RegexPatterns.regex_revertInlineImg, RegexPatterns.repl_revertInlineImg);
    }

    // TODO: just temporary stuff because it has to be done in the client side
    protected String htmlToText(String s) {
        s = s.replaceAll("\n", " ");
        s = s.replaceAll("(?si)<br\\s*?/?>", "\n");
        s = s.replaceAll("(?si)</div\\s*?>", "\n");
        s = s.replaceAll("(\\w)<.*?>(\\w)", "$1 $2");
        s = s.replaceAll("<.*?>", "");
        s = s.replaceAll("[ \t]+", " ");
        return s;
    }

    /**
     * Get the attachments stored in the registry.
     *
     * @param action
     * @return A list of stored attachments
     * @throws HupaException
     */
    @SuppressWarnings("rawtypes")
    protected List getAttachments(SendMessageAction action) throws MessagingException, HupaException {
        FileItemRegistry registry = SessionUtils.getSessionRegistry(logger, httpSessionProvider.get());
        List<MessageAttachment> attachments = action.getMessage().getMessageAttachments();

        ArrayList<FileItem> items = new ArrayList<FileItem>();
        if (attachments != null && attachments.size() > 0) {
            for (MessageAttachment attachment : attachments) {
                FileItem fItem = registry.get(attachment.getName());
                if (fItem != null)
                    items.add(fItem);
            }
            logger.debug("Found " + items.size() + " attachmets in the registry.");
        }
        return items;
    }

    /**
     * Remove attachments from the registry
     *
     * @param action
     * @throws MessagingException
     * @throws ActionException
     */
    protected void resetAttachments(SendMessageAction action) throws MessagingException {
        SmtpMessage msg = action.getMessage();
        List<MessageAttachment> attachments = msg.getMessageAttachments();
        if (attachments != null && !attachments.isEmpty()) {
            for (MessageAttachment attach : attachments)
                SessionUtils.getSessionRegistry(logger, httpSessionProvider.get()).remove(attach.getName());
        }
    }

    /**
     * Send the message using SMTP, if the configuration uses authenticated SMTP, it uses
     * the user stored in session to get the given login and password.
     *
     * @param user
     * @param session
     * @param message
     * @throws MessagingException
     */
    protected void sendMessage(User user, Message message) throws MessagingException {
        cache.sendMessage(message);
        logger.info("Send message from " + message.getFrom()[0].toString());
    }

    /**
     * Save the message in the sent folder
     *
     * @param user
     * @param message
     * @throws MessagingException
     * @throws IOException
     */
    protected void saveSentMessage(User user, Message message) throws MessagingException, IOException {
        IMAPStore iStore = cache.get(user);
        IMAPFolder folder = (IMAPFolder) iStore.getFolder(user.getSettings().getSentFolderName());

        if (folder.exists() || folder.create(IMAPFolder.READ_WRITE)) {
            if (folder.isOpen() == false) {
                folder.open(Folder.READ_WRITE);
            }

            // It is necessary to copy the message, before putting it
            // in the sent folder. If not, it is not guaranteed that it is
            // stored in ascii and is not possible to get the attachments
            // size. message.saveChanges() doesn't fix the problem.
            // There are tests which demonstrate this.
            message = new MimeMessage((MimeMessage) message);

            message.setFlag(Flag.SEEN, true);
            folder.appendMessages(new Message[] { message });

            try {
                folder.close(false);
            } catch (MessagingException e) {
                // we don't care on close
            }
        }
    }

    /**
     * Fill the body of a message already created.
     * The result message depends on the information given.
     *
     * @param message
     * @param text
     * @param html
     * @param parts
     * @return The composed message
     * @throws MessagingException
     * @throws IOException
     */
    @SuppressWarnings("rawtypes")
    public static Message composeMessage(Message message, String text, String html, List parts)
            throws MessagingException, IOException {

        MimeBodyPart txtPart = null;
        MimeBodyPart htmlPart = null;
        MimeMultipart mimeMultipart = null;

        if (text == null && html == null) {
            text = "";
        }
        if (text != null) {
            txtPart = new MimeBodyPart();
            txtPart.setContent(text, "text/plain; charset=UTF-8");
        }
        if (html != null) {
            htmlPart = new MimeBodyPart();
            htmlPart.setContent(html, "text/html; charset=UTF-8");
        }
        if (html != null && text != null) {
            mimeMultipart = new MimeMultipart();
            mimeMultipart.setSubType("alternative");
            mimeMultipart.addBodyPart(txtPart);
            mimeMultipart.addBodyPart(htmlPart);
        }

        if (parts == null || parts.isEmpty()) {
            if (mimeMultipart != null) {
                message.setContent(mimeMultipart);
            } else if (html != null) {
                message.setText(html);
                message.setHeader("Content-type", "text/html");
            } else if (text != null) {
                message.setText(text);
            }
        } else {
            MimeBodyPart bodyPart = new MimeBodyPart();
            if (mimeMultipart != null) {
                bodyPart.setContent(mimeMultipart);
            } else if (html != null) {
                bodyPart.setText(html);
                bodyPart.setHeader("Content-type", "text/html");
            } else if (text != null) {
                bodyPart.setText(text);
            }
            Multipart multipart = new MimeMultipart();
            multipart.addBodyPart(bodyPart);
            for (Object attachment : parts) {
                if (attachment instanceof FileItem) {
                    multipart.addBodyPart(MessageUtils.fileitemToBodypart((FileItem) attachment));
                } else {
                    multipart.addBodyPart((BodyPart) attachment);
                }
            }
            message.setContent(multipart);
        }

        message.saveChanges();
        return message;

    }

    /**
     * DataStore which wrap a FileItem
     *
     */
    public static class FileItemDataStore implements DataSource {

        private FileItem item;

        public FileItemDataStore(FileItem item) {
            this.item = item;
        }

        /*
         * (non-Javadoc)
         * @see javax.activation.DataSource#getContentType()
         */
        public String getContentType() {
            return item.getContentType();
        }

        /*
         * (non-Javadoc)
         * @see javax.activation.DataSource#getInputStream()
         */
        public InputStream getInputStream() throws IOException {
            return item.getInputStream();
        }

        /*
         * (non-Javadoc)
         * @see javax.activation.DataSource#getName()
         */
        public String getName() {
            String fullName = item.getName();

            // Strip path from file
            int index = fullName.lastIndexOf(File.separator);
            if (index == -1) {
                return fullName;
            } else {
                return fullName.substring(index + 1, fullName.length());
            }
        }

        /*
         * (non-Javadoc)
         * @see javax.activation.DataSource#getOutputStream()
         */
        public OutputStream getOutputStream() throws IOException {
            return null;
        }

    }

}