Java tutorial
/* * Copyright (c) 2008-2011, Martijn Brinkers, Djigzo. * * This file is part of Djigzo email encryption. * * Djigzo is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3, 19 November 2007 as published by the Free Software * Foundation. * * Djigzo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public * License along with Djigzo. If not, see <http://www.gnu.org/licenses/> * * Additional permission under GNU AGPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, * wsdl4j-1.6.1.jar (or modified versions of these libraries), * containing parts covered by the terms of Eclipse Public License, * tyrex license, freemarker license, dom4j license, mx4j license, * Spice Software License, Common Development and Distribution License * (CDDL), Common Public License (CPL) the licensors of this Program grant * you additional permission to convey the resulting work. */ package mitm.common.mail; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import java.util.Enumeration; import javax.mail.BodyPart; import javax.mail.Header; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.internet.InternetHeaders; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimePart; import mitm.common.mail.matcher.HeaderMatcher; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BodyPartUtils { private static Logger logger = LoggerFactory.getLogger(BodyPartUtils.class); public static MimeBodyPart makeContentBodyPart(MimePart sourcePart, HeaderMatcher matcher) throws MessagingException, IOException { MimeBodyPart newBodyPart = new MimeBodyPart(); newBodyPart.setContent(sourcePart.getContent(), sourcePart.getContentType()); HeaderUtils.copyHeaders(sourcePart, newBodyPart, matcher); return newBodyPart; } public static MimeBodyPart makeContentBodyPartRaw(MimeBodyPart sourceMessage, HeaderMatcher matcher) throws IOException, MessagingException { /* * getRawInputStream() can throw a MessagingException when the source message is a MimeMessage * that is created in code (ie. not from a stream) */ InputStream messageStream = sourceMessage.getRawInputStream(); MimeBodyPart newBodyPart = new MimeBodyPart(messageStream); HeaderUtils.copyHeaders(sourceMessage, newBodyPart, matcher); return newBodyPart; } public static MimeBodyPart makeContentBodyPartRaw(MimeMessage sourceMessage, HeaderMatcher matcher) throws IOException, MessagingException { /* * getRawInputStream() can throw a MessagingException when the source message is a MimeMessage * that is created in code (ie. not from a stream) */ InputStream messageStream = sourceMessage.getRawInputStream(); MimeBodyPart newBodyPart = new MimeBodyPart(messageStream); HeaderUtils.copyHeaders(sourceMessage, newBodyPart, matcher); return newBodyPart; } /** * This is the only way I know of to create a new MimeBodyPart from another MimeBodyPart which is safe * for signed email. All other methods break the signature when quoted printable soft breaks are used. * * example of quoted printable soft breaks: * * Content-Transfer-Encoding: quoted-printable * soft break example = * another line = * * All other methods will re-encode and removes the soft breaks. * * @param sourceMessage * @param matcher * @return * @throws IOException * @throws MessagingException */ @SuppressWarnings("unchecked") public static MimeBodyPart makeContentBodyPartRawBytes(MimeBodyPart sourceMessage, HeaderMatcher matcher) throws IOException, MessagingException { /* * getRawInputStream() can throw a MessagingException when the source message is a MimeMessage * that is created in code (ie. not from a stream) */ InputStream messageStream = sourceMessage.getRawInputStream(); byte[] rawMessage = IOUtils.toByteArray(messageStream); InternetHeaders destinationHeaders = new InternetHeaders(); Enumeration<Header> sourceHeaders = sourceMessage.getAllHeaders(); HeaderUtils.copyHeaders(sourceHeaders, destinationHeaders, matcher); MimeBodyPart newBodyPart = new MimeBodyPart(destinationHeaders, rawMessage); return newBodyPart; } /** * This is the only way I know of to create a new MimeBodyPart from another Message which is safe * for signed email. All other methods break the signature when quoted printable soft breaks are used. * * example of quoted printable soft breaks: * * Content-Transfer-Encoding: quoted-printable * soft break example = * another line = * * All other methods will re-encode and removes the soft breaks. * * @param sourceMessage * @param matcher * @return * @throws IOException * @throws MessagingException */ @SuppressWarnings("unchecked") public static MimeBodyPart makeContentBodyPartRawBytes(MimeMessage sourceMessage, HeaderMatcher matcher) throws IOException, MessagingException { /* * getRawInputStream() can throw a MessagingException when the source message is a MimeMessage * that is created in code (ie. not from a stream) */ InputStream messageStream = sourceMessage.getRawInputStream(); byte[] rawMessage = IOUtils.toByteArray(messageStream); InternetHeaders destinationHeaders = new InternetHeaders(); Enumeration<Header> sourceHeaders = sourceMessage.getAllHeaders(); HeaderUtils.copyHeaders(sourceHeaders, destinationHeaders, matcher); MimeBodyPart newBodyPart = new MimeBodyPart(destinationHeaders, rawMessage); return newBodyPart; } public static MimeBodyPart makeContentBodyPart(MimeBodyPart sourcePart, HeaderMatcher matcher) throws MessagingException, IOException { MimeBodyPart mimeBodyPart = null; try { mimeBodyPart = makeContentBodyPartRawBytes(sourcePart, matcher); } catch (IOException e) { logger.error("makeContentBodyPartRaw failed. Trying makeContentBodyPartStandard.", e); } catch (MessagingException e) { /* * makeContentBodyPartRaw is not supported if the sourcePart is a MimeMessage that was * created in code ie. not from a stream */ logger.debug("makeContentBodyPartRaw failed. Trying makeContentBodyPartStandard.", e); } if (mimeBodyPart == null) { mimeBodyPart = makeContentBodyPart((MimePart) sourcePart, matcher); } return mimeBodyPart; } public static MimeBodyPart makeContentBodyPart(MimeMessage sourcePart, HeaderMatcher matcher) throws MessagingException, IOException { MimeBodyPart mimeBodyPart = null; try { mimeBodyPart = makeContentBodyPartRawBytes(sourcePart, matcher); } catch (IOException e) { logger.error("makeContentBodyPartRaw failed. Trying makeContentBodyPartStandard.", e); } catch (MessagingException e) { /* * makeContentBodyPartRaw is not supported if the sourcePart is a MimeMessage that was * created in code ie. not from a stream */ logger.debug("makeContentBodyPartRaw failed. Trying makeContentBodyPartStandard.", e); } if (mimeBodyPart == null) { mimeBodyPart = makeContentBodyPart((MimePart) sourcePart, matcher); } return mimeBodyPart; } /** * Creates a MimeMessage from a Part. If part is an instance of MimeMessage the part is returned as a new * MimeMessage if clone is true. If clone is false the Part is cast to MimeMessage and returned. */ public static MimeMessage toMessage(Part part, boolean clone) throws MessagingException, IOException { if (part instanceof MimeMessage) { return clone ? MailUtils.cloneMessage((MimeMessage) part) : (MimeMessage) part; } byte[] bytes = MailUtils.partToByteArray(part); MimeMessage message = MailUtils.byteArrayToMessage(bytes); return message; } public static MimeMessage toMessage(Part part) throws MessagingException, IOException { return toMessage(part, false /* do not clone */); } /** * Creates a MimeBodyPart from a Part. If part is an instance of MimeBodyPart the part is returned without * any modification. */ public static MimeBodyPart toMimeBodyPart(Part part) throws MessagingException, IOException { if (part instanceof MimeBodyPart) { return (MimeBodyPart) part; } byte[] bytes = MailUtils.partToByteArray(part); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); MimeBodyPart mimeBodyPart = new MimeBodyPart(bis); return mimeBodyPart; } /** * Creates a MimeBodyPart with the provided message attached as a RFC822 attachment. */ public static MimeBodyPart toRFC822(MimeMessage message, String filename) throws MessagingException { MimeBodyPart bodyPart = new MimeBodyPart(); bodyPart.setContent(message, "message/rfc822"); /* somehow the content-Type header is not set so we need to set it ourselves */ bodyPart.setHeader("Content-Type", "message/rfc822"); bodyPart.setDisposition(Part.INLINE); bodyPart.setFileName(filename); return bodyPart; } /** * Extracts the message from the RFC822 attachment. * @throws MessagingException * @throws IOException */ public static MimeMessage extractFromRFC822(Part rfc822) throws IOException, MessagingException { if (!rfc822.isMimeType("message/rfc822")) { throw new MessagingException("Part is-not-a message/rfc822 but " + rfc822.getContentType()); } return new MimeMessage(MailSession.getDefaultSession(), rfc822.getInputStream()); } /** * Searches for an embedded RFC822 messages. Returns the first embedded RFC822 it finds. * Only one level deep is searched. * * Returns null if there are no embedded message. */ public static MimeMessage searchForRFC822(MimeMessage message) throws MessagingException, IOException { /* * Fast fail. Only multipart mixed messages are supported. */ if (!message.isMimeType("multipart/mixed")) { return null; } Multipart mp; try { mp = (Multipart) message.getContent(); } catch (IOException e) { throw new MessagingException("Error getting message content.", e); } MimeMessage embeddedMessage = null; for (int i = 0; i < mp.getCount(); i++) { BodyPart part = mp.getBodyPart(i); if (part.isMimeType("message/rfc822")) { embeddedMessage = BodyPartUtils.extractFromRFC822(part); break; } } return embeddedMessage; } /** * Returns the plain text body of the message. */ public static String getPlainBody(final MimeMessage message) throws MessagingException, IOException { return getPlainBodyAndAttachments(message, null); } /** * Returns the plain text body of the message and it's attachments. The attachments collection is cleared before use. */ public static String getPlainBodyAndAttachments(final MimeMessage message, Collection<Part> attachments) throws MessagingException, IOException { String body = null; for (int maxLevel = 1; maxLevel <= 2; maxLevel++) { if (attachments != null) { attachments.clear(); } body = getPlainBodyAndAttachments(message, attachments, 0, maxLevel); if (body != null) { break; } } return body; } private static String getPlainBodyAndAttachments(final Part part, Collection<Part> attachments, int level, int maxLevel) throws MessagingException, IOException { String body = null; if (part.isMimeType("text/plain")) { body = (String) part.getContent(); } else { /* * Maximum level deep */ if (level <= maxLevel && part.isMimeType("multipart/*")) { Multipart mp = (Multipart) part.getContent(); int partCount = mp.getCount(); for (int i = 0; i < partCount; i++) { Part child = mp.getBodyPart(i); if (body == null) { body = getPlainBodyAndAttachments(child, attachments, level + 1, maxLevel); if (body == null && part.isMimeType("multipart/mixed")) { if (attachments != null) { attachments.add(child); } } } else if (part.isMimeType("multipart/mixed")) { if (attachments != null) { attachments.add(child); } } } } } return body; } }