Java tutorial
/**************************************************************** * 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.utils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.mail.Address; import javax.mail.BodyPart; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeUtility; import org.apache.commons.fileupload.FileItem; import org.apache.commons.logging.Log; import org.apache.hupa.shared.data.MessageAttachmentImpl; import org.apache.hupa.shared.domain.MessageAttachment; /** * Utility methods in server side */ public class MessageUtils { /** * Get a Address array for a set of address passed as arguments * * @param addresses * @return Address array * @throws AddressException */ public static Address[] getRecipients(String... addresses) throws AddressException { return getRecipients(Arrays.asList(addresses)); } /** * Get a Address array for the given ArrayList * * @param recipients * @return addressArray * @throws AddressException */ public static Address[] getRecipients(List<String> recipients) throws AddressException { if (recipients == null) { return new InternetAddress[] {}; } Address[] array = new Address[recipients.size()]; for (int i = 0; i < recipients.size(); i++) { array[i] = new InternetAddress(encodeEmail(recipients.get(i))); } return array; } /** * Extract the attachments present in a mime message * * @param logger * @param content * @return A list of body parts of the attachments * @throws MessagingException * @throws IOException */ static public List<BodyPart> extractMessageAttachments(Log logger, Object content) throws MessagingException, IOException { ArrayList<BodyPart> ret = new ArrayList<BodyPart>(); if (content instanceof Multipart) { Multipart part = (Multipart) content; for (int i = 0; i < part.getCount(); i++) { BodyPart bodyPart = part.getBodyPart(i); String fileName = bodyPart.getFileName(); String[] contentId = bodyPart.getHeader("Content-ID"); if (bodyPart.isMimeType("multipart/*")) { ret.addAll(extractMessageAttachments(logger, bodyPart.getContent())); } else { if (contentId != null || fileName != null) { ret.add(bodyPart); } } } } else { logger.error("Unknown content: " + content.getClass().getName()); } return ret; } static public List<BodyPart> extractInlineImages(Log logger, Object content) throws MessagingException, IOException { ArrayList<BodyPart> ret = new ArrayList<BodyPart>(); for (BodyPart attach : extractMessageAttachments(logger, content)) { if (attach.getHeader("Content-ID") != null && attach.getContentType().startsWith("image/")) ret.add(attach); } return ret; } /** * Handle the parts of the given message. The method will call itself * recursively to handle all nested parts * * @param message the MimeMessage * @param content the current processing Content * @param sbPlain the StringBuffer to fill with text * @param attachmentList ArrayList with attachments * @throws UnsupportedEncodingException * @throws MessagingException * @throws IOException */ public static boolean handleParts(Message message, Object content, StringBuffer sbPlain, ArrayList<MessageAttachment> attachmentList) throws UnsupportedEncodingException, MessagingException, IOException { boolean isHTML = false; if (content instanceof String) { if (message.getContentType().toLowerCase().startsWith("text/html")) { isHTML = true; } else { isHTML = false; } sbPlain.append((String) content); } else if (content instanceof Multipart) { Multipart mp = (Multipart) content; String multipartContentType = mp.getContentType().toLowerCase(); String text = null; if (multipartContentType.startsWith("multipart/alternative")) { isHTML = handleMultiPartAlternative(mp, sbPlain); } else { for (int i = 0; i < mp.getCount(); i++) { Part part = mp.getBodyPart(i); String contentType = part.getContentType().toLowerCase(); Boolean bodyRead = sbPlain.length() > 0; if (!bodyRead && contentType.startsWith("text/plain")) { isHTML = false; text = (String) part.getContent(); } else if (!bodyRead && contentType.startsWith("text/html")) { isHTML = true; text = (String) part.getContent(); } else if (!bodyRead && contentType.startsWith("message/rfc822")) { // Extract the message and pass it MimeMessage msg = (MimeMessage) part.getDataHandler().getContent(); isHTML = handleParts(msg, msg.getContent(), sbPlain, attachmentList); } else { if (part.getFileName() != null) { // Inline images are not added to the attachment // list // TODO: improve the in-line images detection if (part.getHeader("Content-ID") == null) { MessageAttachment attachment = new MessageAttachmentImpl(); attachment.setName(MimeUtility.decodeText(part.getFileName())); attachment.setContentType(part.getContentType()); attachment.setSize(part.getSize()); attachmentList.add(attachment); } } else { isHTML = handleParts(message, part.getContent(), sbPlain, attachmentList); } } } if (text != null) sbPlain.append(text); } } return isHTML; } private static boolean handleMultiPartAlternative(Multipart mp, StringBuffer sbPlain) throws MessagingException, IOException { String text = null; boolean isHTML = false; for (int i = 0; i < mp.getCount(); i++) { Part part = mp.getBodyPart(i); String contentType = part.getContentType().toLowerCase(); // we prefer html if (text == null && contentType.startsWith("text/plain")) { isHTML = false; text = (String) part.getContent(); } else if (contentType.startsWith("text/html")) { isHTML = true; text = (String) part.getContent(); } } sbPlain.append(text); return isHTML; } /** * Loop over MuliPart and write the content to the Outputstream if a * attachment with the given name was found. * * @param logger * The logger to use * @param content * Content which should checked for attachments * @param attachmentName * The attachmentname or the unique id for the searched attachment * @throws MessagingException * @throws IOException */ public static Part handleMultiPart(Log logger, Object content, String attachmentName) throws MessagingException, IOException { if (content instanceof Multipart) { Multipart part = (Multipart) content; for (int i = 0; i < part.getCount(); i++) { Part bodyPart = part.getBodyPart(i); String fileName = bodyPart.getFileName(); String[] contentId = bodyPart.getHeader("Content-ID"); if (bodyPart.isMimeType("multipart/*")) { Part p = handleMultiPart(logger, bodyPart.getContent(), attachmentName); if (p != null) return p; } else { if (contentId != null) { for (String id : contentId) { id = id.replaceAll("^.*<(.+)>.*$", "$1"); System.out.println(attachmentName + " " + id); if (attachmentName.equals(id)) return bodyPart; } } if (fileName != null) { if (cleanName(attachmentName).equalsIgnoreCase(cleanName(MimeUtility.decodeText(fileName)))) return bodyPart; } } } } else { logger.error("Unknown content: " + content.getClass().getName()); } return null; } private static String cleanName(String s) { return s.replaceAll("[^\\w .+-]", ""); } /** * Convert a FileItem to a BodyPart * * @param item * @return message body part * @throws MessagingException */ public static BodyPart fileitemToBodypart(FileItem item) throws MessagingException { MimeBodyPart messageBodyPart = new MimeBodyPart(); DataSource source = new FileItemDataStore(item); messageBodyPart.setDataHandler(new DataHandler(source)); messageBodyPart.setFileName(source.getName()); return messageBodyPart; } /** * 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; } } /** * Decode iso-xxxx strings present in subjects and emails like: * * =?ISO-8859-1?Q?No=20hay=20ma=F1ana?= <hello@hupa.org> */ public static String decodeText(String s) { String ret = s; try { ret = MimeUtility.decodeText(s); } catch (UnsupportedEncodingException e) { System.out.println(e.getMessage()); } ret = ret // Remove quotes around names in email addresses .replaceFirst("^[<\"' ]+([^\"<>]*)[>\"' ]+<", "$1 <"); return ret; } /** * Encode non ascii characters present in emails like: * * =?ISO-8859-1?Q?No=20hay=20ma=F1ana?= <hello@hupa.org> */ public static String encodeEmail(String s) { if (s == null) { return s; } Pattern p = Pattern.compile("^\\s*(.*?)\\s*(<[^>]+>)\\s*"); Matcher m = p.matcher(s); return m.matches() ? encodeTexts(m.group(1)) + " " + m.group(2) : s; } /** * Encode non ascii characters present in email headers */ public static String encodeTexts(String s) { String ret = s; if (s != null) { try { ret = MimeUtility.encodeText(s, "ISO-8859-1", null); } catch (UnsupportedEncodingException e) { } } return ret; } }