blob: 13ea54bbc28ed552015095a2f1f5ae6ef346d9fc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.commons;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.activation.CommandMap;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.activation.MailcapCommandMap;
import javax.activation.MimetypesFileTypeMap;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
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.MimeMultipart;
import javax.mail.internet.MimePart;
import javax.mail.internet.MimeUtility;
import javax.mail.util.ByteArrayDataSource;
import org.eclipse.scout.commons.exception.ProcessingException;
import org.eclipse.scout.commons.logger.IScoutLogger;
import org.eclipse.scout.commons.logger.ScoutLogManager;
@SuppressWarnings("restriction")
public final class MailUtility {
public static final IScoutLogger LOG = ScoutLogManager.getLogger(MailUtility.class);
public static final String CONTENT_TYPE_ID = "Content-Type";
public static final String CONTENT_TYPE_TEXT_HTML = "text/html; charset=\"UTF-8\"";
public static final String CONTENT_TYPE_TEXT_PLAIN = "text/plain; charset=\"UTF-8\"";
public static final String CONTENT_TYPE_MESSAGE_RFC822 = "message/rfc822";
public static final String CONTENT_TYPE_MULTIPART = "alternative";
public static final Pattern wordPatternItem = Pattern.compile("item\\d{3,4}\\.xml");
public static final Pattern wordPatternProps = Pattern.compile("props\\d{3,4}\\.xml");
private static MailUtility instance = new MailUtility();
private MailUtility() {
}
/**
* Container for the mail body in plain text and html.
* For the correct html representation it contains also a list of files referenced in the html.
*/
public class MailMessage {
private String m_plainText;
private String m_htmlText;
private List<File> m_htmlAttachmentList;
public MailMessage(String plainText, String htmlText, List<File> htmlAttachmentList) {
m_plainText = plainText;
m_htmlText = htmlText;
m_htmlAttachmentList = htmlAttachmentList;
}
public String getPlainText() {
return m_plainText;
}
public String getHtmlText() {
return m_htmlText;
}
public List<File> getHtmlAttachmentList() {
return m_htmlAttachmentList;
}
}
public static Part[] getBodyParts(Part message) throws ProcessingException {
return instance.getBodyPartsImpl(message);
}
private Part[] getBodyPartsImpl(Part message) throws ProcessingException {
List<Part> bodyCollector = new ArrayList<Part>();
collectMailPartsReqImpl(message, bodyCollector, null, null);
return bodyCollector.toArray(new Part[bodyCollector.size()]);
}
public static Part[] getAttachmentParts(Part message) throws ProcessingException {
return instance.getAttachmentPartsImpl(message);
}
private Part[] getAttachmentPartsImpl(Part message) throws ProcessingException {
List<Part> attachmentCollector = new ArrayList<Part>();
collectMailPartsReqImpl(message, null, attachmentCollector, null);
return attachmentCollector.toArray(new Part[attachmentCollector.size()]);
}
public static void collectMailParts(Part message, List<Part> bodyCollector, List<Part> attachmentCollector) throws ProcessingException {
collectMailParts(message, bodyCollector, attachmentCollector, null);
}
public static void collectMailParts(Part message, List<Part> bodyCollector, List<Part> attachmentCollector, List<Part> inlineAttachmentCollector) throws ProcessingException {
instance.collectMailPartsReqImpl(message, bodyCollector, attachmentCollector, inlineAttachmentCollector);
}
/**
* Collects the body, attachment and inline attachment parts from the provided part.
* <p>
* A single collector can be null in order to collect only the relevant parts.
*
* @param part
* Part
* @param bodyCollector
* Body collector (optional)
* @param attachmentCollector
* Attachment collector (optional)
* @param inlineAttachmentCollector
* Inline attachment collector (optional)
* @throws ProcessingException
*/
private void collectMailPartsReqImpl(Part part, List<Part> bodyCollector, List<Part> attachmentCollector, List<Part> inlineAttachmentCollector) throws ProcessingException {
if (part == null) {
return;
}
try {
String disp = part.getDisposition();
if (disp != null && disp.equalsIgnoreCase(Part.ATTACHMENT)) {
if (attachmentCollector != null) {
attachmentCollector.add(part);
}
}
else if (part.getContent() instanceof Multipart) {
Multipart multiPart = (Multipart) part.getContent();
for (int i = 0; i < multiPart.getCount(); i++) {
collectMailPartsReqImpl(multiPart.getBodyPart(i), bodyCollector, attachmentCollector, inlineAttachmentCollector);
}
}
else {
if (part.isMimeType(CONTENT_TYPE_TEXT_PLAIN)) {
if (bodyCollector != null) {
bodyCollector.add(part);
}
}
else if (part.isMimeType(CONTENT_TYPE_TEXT_HTML)) {
if (bodyCollector != null) {
bodyCollector.add(part);
}
}
else if (part.isMimeType(CONTENT_TYPE_MESSAGE_RFC822) && part.getContent() instanceof MimeMessage) {
// its a MIME message in rfc822 format as attachment therefore we have to set the filename for the attachment correctly.
if (attachmentCollector != null) {
MimeMessage msg = (MimeMessage) part.getContent();
String filteredSubjectText = StringUtility.filterText(msg.getSubject(), "a-zA-Z0-9_-", "");
String fileName = (StringUtility.hasText(filteredSubjectText) ? filteredSubjectText : "originalMessage") + ".eml";
RFCWrapperPart wrapperPart = new RFCWrapperPart(part, fileName);
attachmentCollector.add(wrapperPart);
}
}
else if (disp != null && disp.equals(Part.INLINE)) {
if (inlineAttachmentCollector != null) {
inlineAttachmentCollector.add(part);
}
}
}
}
catch (ProcessingException e) {
throw e;
}
catch (Throwable t) {
throw new ProcessingException("Unexpected: ", t);
}
}
/**
* @param part
* @return the plainText part encoded with the encoding given in the MIME header or UTF-8 encoded or null if the
* plainText Part is not given
* @throws ProcessingException
*/
public static String getPlainText(Part part) throws ProcessingException {
return instance.getPlainTextImpl(part);
}
private String getPlainTextImpl(Part part) throws ProcessingException {
String text = null;
try {
Part[] bodyParts = getBodyPartsImpl(part);
Part plainTextPart = getPlainTextPart(bodyParts);
if (plainTextPart instanceof MimePart) {
MimePart mimePart = (MimePart) plainTextPart;
byte[] content = IOUtility.getContent(mimePart.getInputStream());
if (content != null) {
try {
text = new String(content, getCharacterEncodingOfMimePart(mimePart));
}
catch (UnsupportedEncodingException e) {
text = new String(content);
}
}
}
}
catch (ProcessingException e) {
throw e;
}
catch (Throwable t) {
throw new ProcessingException("Unexpected: ", t);
}
return text;
}
public static Part getHtmlPart(Part[] bodyParts) throws ProcessingException {
for (Part p : bodyParts) {
try {
if (p != null && p.isMimeType(CONTENT_TYPE_TEXT_HTML)) {
return p;
}
}
catch (Throwable t) {
throw new ProcessingException("Unexpected: ", t);
}
}
return null;
}
public static Part getPlainTextPart(Part[] bodyParts) throws ProcessingException {
for (Part p : bodyParts) {
try {
if (p != null && p.isMimeType(CONTENT_TYPE_TEXT_PLAIN)) {
return p;
}
}
catch (Throwable t) {
throw new ProcessingException("Unexpected: ", t);
}
}
return null;
}
public static DataSource createDataSource(File file) throws ProcessingException {
try {
int indexDot = file.getName().lastIndexOf(".");
if (indexDot > 0) {
String fileName = file.getName();
String ext = fileName.substring(indexDot + 1);
return instance.createDataSourceImpl(new FileInputStream(file), fileName, ext);
}
else {
return null;
}
}
catch (Throwable t) {
throw new ProcessingException("Unexpected: ", t);
}
}
public static DataSource createDataSource(InputStream inStream, String fileName, String fileExtension) throws ProcessingException {
return instance.createDataSourceImpl(inStream, fileName, fileExtension);
}
/**
* @param inStream
* @param fileName
* e.g. "file.txt"
* @param fileExtension
* e.g. "txt", "jpg"
* @return
* @throws ProcessingException
*/
private DataSource createDataSourceImpl(InputStream inStream, String fileName, String fileExtension) throws ProcessingException {
try {
String mimeType = getContentTypeForExtension(fileExtension);
if (mimeType == null) {
mimeType = "application/octet-stream";
}
ByteArrayDataSource item = new ByteArrayDataSource(inStream, mimeType);
item.setName(fileName);
return item;
}
catch (Throwable t) {
throw new ProcessingException("Unexpected: ", t);
}
}
public static MailMessage extractMailMessageFromWordArchive(File archiveFile) {
return instance.extractMailMessageFromWordArchiveInternal(archiveFile);
}
private MailMessage extractMailMessageFromWordArchiveInternal(File archiveFile) {
MailMessage mailMessage = null;
File tempDir = extractWordArchive(archiveFile);
String simpleName = extractSimpleNameFromWordArchive(archiveFile);
String messagePlainText = extractPlainTextFromWordArchiveInternal(tempDir, simpleName);
String messageHtml = extractHtmlFromWordArchiveInternal(tempDir, simpleName);
// replace directory entry
// replace all paths to the 'files directory' with the root directory
File attachmentFolder = null;
if (tempDir.isDirectory()) {
for (File file : tempDir.listFiles()) {
if (file.isDirectory() && file.getName().startsWith(simpleName)) {
attachmentFolder = file;
break;
}
}
}
String folderName = null;
if (attachmentFolder != null) {
folderName = attachmentFolder.getName();
}
messageHtml = messageHtml.replaceAll(folderName + "/", "");
messageHtml = removeWordTags(messageHtml);
// now loop through the directory and search all the files needed for a correct representation of the html mail
List<File> attachmentList = new ArrayList<File>();
if (attachmentFolder != null) {
for (File attFile : attachmentFolder.listFiles()) {
// exclude Microsoft Word specific directory file. This is only used to edit HTML in Word.
if (!attFile.isDirectory() && !isWordSpecificFile(attFile.getName())) {
attachmentList.add(attFile);
}
}
}
mailMessage = new MailMessage(messagePlainText, messageHtml, attachmentList);
return mailMessage;
}
private String extractHtmlFromWordArchiveInternal(File dir, String simpleName) {
String txt = null;
try {
txt = extractTextFromWordArchiveInternal(dir, simpleName, "html");
}
catch (Exception e) {
LOG.error("Error occured while trying to extract plain text file", e);
}
return txt;
}
private String extractSimpleNameFromWordArchive(File archiveFile) {
String simpleName = archiveFile.getName();
if (archiveFile.getName().lastIndexOf('.') != -1) {
simpleName = archiveFile.getName().substring(0, archiveFile.getName().lastIndexOf('.'));
}
return simpleName;
}
private File extractWordArchive(File archiveFile) {
File tempDir = null;
try {
tempDir = IOUtility.createTempDirectory("");
FileUtility.extractArchive(archiveFile, tempDir);
}
catch (Exception e) {
LOG.error("Error occured while trying to extract word archive", e);
}
return tempDir;
}
public static String extractPlainTextFromWordArchive(File archiveFile) {
return instance.extractPlainTextFromWordArchiveInternal(archiveFile);
}
private String extractPlainTextFromWordArchiveInternal(File archiveFile) {
File tempDir = extractWordArchive(archiveFile);
String simpleName = extractSimpleNameFromWordArchive(archiveFile);
return extractPlainTextFromWordArchiveInternal(tempDir, simpleName);
}
private String extractPlainTextFromWordArchiveInternal(File dir, String simpleName) {
String plainText = null;
try {
plainText = extractTextFromWordArchiveInternal(dir, simpleName, "txt");
}
catch (Exception e) {
LOG.error("Error occured while trying to extract plain text file", e);
}
return plainText;
}
private String extractTextFromWordArchiveInternal(File dir, String simpleName, String fileType) throws ProcessingException, IOException {
String txt = null;
File plainTextFile = new File(dir, simpleName + "." + fileType);
if (plainTextFile.exists() && plainTextFile.canRead()) {
txt = IOUtility.getContentInEncoding(plainTextFile.getPath(), "UTF-8");
}
return txt;
}
/**
* Create {@link MimeMessage} from plain text fields.
*
* @rn aho, 19.01.2009
*/
public static MimeMessage createMimeMessage(String[] toRecipients, String sender, String subject, String bodyTextPlain, DataSource[] attachements) throws ProcessingException {
return instance.createMimeMessageInternal(toRecipients, null, null, sender, subject, bodyTextPlain, attachements);
}
/**
* Create {@link MimeMessage} from plain text fields.
*
* @rn aho, 19.01.2009
*/
public static MimeMessage createMimeMessage(String[] toRecipients, String[] ccRecipients, String[] bccRecipients, String sender, String subject, String bodyTextPlain, DataSource[] attachements) throws ProcessingException {
return instance.createMimeMessageInternal(toRecipients, ccRecipients, bccRecipients, sender, subject, bodyTextPlain, attachements);
}
private MimeMessage createMimeMessageInternal(String[] toRecipients, String[] ccRecipients, String[] bccRecipients, String sender, String subject, String bodyTextPlain, DataSource[] attachements) throws ProcessingException {
try {
MimeMessage msg = MailUtility.createMimeMessage(bodyTextPlain, null, attachements);
if (sender != null) {
InternetAddress addrSender = new InternetAddress(sender);
msg.setFrom(addrSender);
msg.setSender(addrSender);
}
msg.setSentDate(new java.util.Date());
//
msg.setSubject(subject, "UTF-8");
//
msg.setRecipients(Message.RecipientType.TO, parseAddresses(toRecipients));
msg.setRecipients(Message.RecipientType.CC, parseAddresses(ccRecipients));
msg.setRecipients(Message.RecipientType.BCC, parseAddresses(bccRecipients));
return msg;
}
catch (ProcessingException pe) {
throw pe;
}
catch (Exception e) {
throw new ProcessingException("Failed to create MimeMessage.", e);
}
}
public static MimeMessage createMimeMessage(String messagePlain, String messageHtml, DataSource[] attachements) throws ProcessingException {
return instance.createMimeMessageInternal(messagePlain, messageHtml, attachements);
}
public static MimeMessage createMimeMessageFromWordArchiveDirectory(File archiveDir, String simpleName, File[] attachments, boolean markAsUnsent) throws ProcessingException {
return instance.createMimeMessageFromWordArchiveInternal(archiveDir, simpleName, attachments, markAsUnsent);
}
public static MimeMessage createMimeMessageFromWordArchive(File archiveFile, File[] attachments) throws ProcessingException {
return createMimeMessageFromWordArchive(archiveFile, attachments, false);
}
public static MimeMessage createMimeMessageFromWordArchive(File archiveFile, File[] attachments, boolean markAsUnsent) throws ProcessingException {
try {
File tempDir = IOUtility.createTempDirectory("");
FileUtility.extractArchive(archiveFile, tempDir);
String simpleName = archiveFile.getName();
if (archiveFile.getName().lastIndexOf('.') != -1) {
simpleName = archiveFile.getName().substring(0, archiveFile.getName().lastIndexOf('.'));
}
return instance.createMimeMessageFromWordArchiveInternal(tempDir, simpleName, attachments, markAsUnsent);
}
catch (ProcessingException pe) {
throw pe;
}
catch (IOException e) {
throw new ProcessingException("Error occured while accessing files", e);
}
}
private MimeMessage createMimeMessageFromWordArchiveInternal(File archiveDir, String simpleName, File[] attachments, boolean markAsUnsent) throws ProcessingException {
try {
File plainTextFile = new File(archiveDir, simpleName + ".txt");
String plainTextMessage = null;
boolean hasPlainText = false;
if (plainTextFile.exists()) {
plainTextMessage = IOUtility.getContentInEncoding(plainTextFile.getPath(), "UTF-8");
hasPlainText = StringUtility.hasText(plainTextMessage);
}
String folderName = null;
List<DataSource> htmlDataSourceList = new ArrayList<DataSource>();
for (File filesFolder : archiveDir.listFiles()) {
// in this archive file, exactly one directory should exist
// word names this directory differently depending on the language
if (filesFolder.isDirectory() && filesFolder.getName().startsWith(simpleName)) {
// we accept the first directory that meets the constraint above
// add all auxiliary files as attachment
folderName = filesFolder.getName();
for (File file : filesFolder.listFiles()) {
// exclude Microsoft Word specific directory file. This is only used to edit HTML in Word.
String filename = file.getName();
if (!isWordSpecificFile(filename)) {
FileDataSource fds = new FileDataSource(file);
htmlDataSourceList.add(fds);
}
}
break;
}
}
File htmlFile = new File(archiveDir, simpleName + ".html");
String htmlMessage = null;
boolean hasHtml = false;
if (htmlFile.exists()) {
htmlMessage = IOUtility.getContentInEncoding(htmlFile.getPath(), "UTF-8");
// replace directory entry
// replace all paths to the 'files directory' with the root directory
htmlMessage = htmlMessage.replaceAll("\"" + folderName + "/", "\"cid:");
htmlMessage = removeWordTags(htmlMessage);
// remove any VML elements
htmlMessage = htmlMessage.replaceAll("<!--\\[if gte vml 1(.*\\r?\\n)*?.*?endif\\]-->", "");
// remove any VML elements part2
htmlMessage = Pattern.compile("<!\\[if !vml\\]>(.*?)<!\\[endif\\]>", Pattern.DOTALL).matcher(htmlMessage).replaceAll("$1");
// remove not referenced attachments
for (Iterator<DataSource> it = htmlDataSourceList.iterator(); it.hasNext();) {
DataSource ds = it.next();
if (!htmlMessage.contains("cid:" + ds.getName())) {
it.remove();
}
}
hasHtml = StringUtility.hasText(htmlMessage);
}
if (!hasPlainText && !hasHtml) {
throw new ProcessingException("message has no body");
}
MimeMessage mimeMessage = new CharsetSafeMimeMessage();
MimePart bodyPart = null;
if (attachments != null && attachments.length > 0) {
MimeMultipart multiPart = new MimeMultipart(); //mixed
mimeMessage.setContent(multiPart);
//add a holder for the body text
MimeBodyPart multiPartBody = new MimeBodyPart();
multiPart.addBodyPart(multiPartBody);
bodyPart = multiPartBody;
//add the attachments
for (File attachment : attachments) {
MimeBodyPart part = new MimeBodyPart();
FileDataSource fds = new FileDataSource(attachment);
DataHandler handler = new DataHandler(fds);
part.setDataHandler(handler);
part.setFileName(MimeUtility.encodeText(attachment.getName(), "UTF-8", null));
multiPart.addBodyPart(part);
}
}
else {
//no attachments -> no need for multipart/mixed element
bodyPart = mimeMessage;
}
if (hasPlainText && hasHtml) {
MimeMultipart alternativePart = new MimeMultipart("alternative");
bodyPart.setContent(alternativePart);
MimeBodyPart plainBodyPart = new MimeBodyPart();
alternativePart.addBodyPart(plainBodyPart);
writePlainBody(plainBodyPart, plainTextMessage);
MimeBodyPart htmlBodyPart = new MimeBodyPart();
alternativePart.addBodyPart(htmlBodyPart);
writeHtmlBody(htmlBodyPart, htmlMessage, htmlDataSourceList);
}
else if (hasPlainText) { //plain text only
writePlainBody(bodyPart, plainTextMessage);
}
else { //html only
writeHtmlBody(bodyPart, htmlMessage, htmlDataSourceList);
}
if (markAsUnsent) {
mimeMessage.setHeader("X-Unsent", "1"); // only supported in Outlook 2010
}
return mimeMessage;
}
catch (MessagingException e) {
throw new ProcessingException("Error occured while creating MIME-message", e);
}
catch (UnsupportedEncodingException e) {
throw new ProcessingException("Error occured while creating MIME-message", e);
}
}
private String removeWordTags(String htmlMessage) {
// remove special/unused files
htmlMessage = htmlMessage.replaceAll("<link rel=File-List href=\"cid:filelist.xml\">", "");
htmlMessage = htmlMessage.replaceAll("<link rel=colorSchemeMapping href=\"cid:colorschememapping.xml\">", "");
htmlMessage = htmlMessage.replaceAll("<link rel=themeData href=\"cid:themedata.thmx\">", "");
htmlMessage = htmlMessage.replaceAll("<link rel=Edit-Time-Data href=\"cid:editdata.mso\">", "");
// remove Microsoft Word tags
htmlMessage = htmlMessage.replaceAll("<!--\\[if gte mso(.*\\r?\\n)*?.*?endif\\]-->", "");
return htmlMessage;
}
/**
* Checks if file is a Microsoft Word specific directory file. They are only used to edit HTML in Word.
*/
private boolean isWordSpecificFile(String filename) {
return filename.equalsIgnoreCase("filelist.xml") ||
filename.equalsIgnoreCase("colorschememapping.xml") ||
filename.equalsIgnoreCase("themedata.thmx") ||
filename.equalsIgnoreCase("header.html") ||
filename.equalsIgnoreCase("editdata.mso") ||
wordPatternItem.matcher(filename).matches() ||
wordPatternProps.matcher(filename).matches();
}
private static void writeHtmlBody(MimePart htmlBodyPart, String htmlMessage, List<DataSource> htmlDataSourceList) throws MessagingException {
Multipart multiPartHtml = new MimeMultipart("related");
htmlBodyPart.setContent(multiPartHtml);
MimeBodyPart part = new MimeBodyPart();
part.setContent(htmlMessage, CONTENT_TYPE_TEXT_HTML);
part.setHeader(CONTENT_TYPE_ID, CONTENT_TYPE_TEXT_HTML);
part.setHeader("Content-Transfer-Encoding", "quoted-printable");
multiPartHtml.addBodyPart(part);
for (DataSource source : htmlDataSourceList) {
part = new MimeBodyPart();
DataHandler handler = new DataHandler(source);
part.setDataHandler(handler);
part.setFileName(source.getName());
part.addHeader("Content-ID", "<" + source.getName() + ">");
multiPartHtml.addBodyPart(part);
}
}
private static void writePlainBody(MimePart plainBodyPart, String plainTextMessage) throws MessagingException {
plainBodyPart.setText(plainTextMessage, "UTF-8");
plainBodyPart.setHeader(CONTENT_TYPE_ID, CONTENT_TYPE_TEXT_PLAIN);
plainBodyPart.setHeader("Content-Transfer-Encoding", "quoted-printable");
}
private MimeMessage createMimeMessageInternal(String bodyTextPlain, String bodyTextHtml, DataSource[] attachements) throws ProcessingException {
try {
CharsetSafeMimeMessage m = new CharsetSafeMimeMessage();
MimeMultipart multiPart = new MimeMultipart();
BodyPart bodyPart = createBodyPart(bodyTextPlain, bodyTextHtml);
if (bodyPart == null) {
return null;
}
multiPart.addBodyPart(bodyPart);
// attachements
if (attachements != null) {
for (DataSource source : attachements) {
MimeBodyPart part = new MimeBodyPart();
DataHandler handler = new DataHandler(source);
part.setDataHandler(handler);
part.setFileName(source.getName());
multiPart.addBodyPart(part);
}
}
m.setContent(multiPart);
return m;
}
catch (Throwable t) {
throw new ProcessingException("Failed to create MimeMessage.", t);
}
}
private BodyPart createBodyPart(String bodyTextPlain, String bodyTextHtml) throws MessagingException {
if (!StringUtility.isNullOrEmpty(bodyTextPlain) && !StringUtility.isNullOrEmpty(bodyTextHtml)) {
// multipart
MimeBodyPart plainPart = new MimeBodyPart();
plainPart.setText(bodyTextPlain, "UTF-8");
plainPart.addHeader(CONTENT_TYPE_ID, CONTENT_TYPE_TEXT_PLAIN);
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setText(bodyTextHtml, "UTF-8");
htmlPart.addHeader(CONTENT_TYPE_ID, CONTENT_TYPE_TEXT_HTML);
Multipart multiPart = new MimeMultipart("alternative");
multiPart.addBodyPart(plainPart);
multiPart.addBodyPart(htmlPart);
MimeBodyPart multiBodyPart = new MimeBodyPart();
multiBodyPart.setContent(multiPart);
return multiBodyPart;
}
else if (!StringUtility.isNullOrEmpty(bodyTextPlain)) {
MimeBodyPart part = new MimeBodyPart();
part.setText(bodyTextPlain, "UTF-8");
part.addHeader(CONTENT_TYPE_ID, CONTENT_TYPE_TEXT_PLAIN);
return part;
}
else if (!StringUtility.isNullOrEmpty(bodyTextHtml)) {
MimeBodyPart part = new MimeBodyPart();
part.setText(bodyTextHtml, "UTF-8");
part.addHeader(CONTENT_TYPE_ID, CONTENT_TYPE_TEXT_HTML);
return part;
}
return null;
}
public static MimeMessage createMessageFromBytes(byte[] bytes) throws ProcessingException {
return instance.createMessageFromBytesImpl(bytes, null);
}
public static MimeMessage createMessageFromBytes(byte[] bytes, Session session) throws ProcessingException {
return instance.createMessageFromBytesImpl(bytes, session);
}
private MimeMessage createMessageFromBytesImpl(byte[] bytes, Session session) throws ProcessingException {
try {
ByteArrayInputStream st = new ByteArrayInputStream(bytes);
return new MimeMessage(session, st);
}
catch (Throwable t) {
throw new ProcessingException("Unexpected: ", t);
}
}
/**
* Adds the provided attachments to the existing mime message.
*
* @param msg
* Mime message to attach files to
* @param attachments
* List of attachments (files).
* @throws ProcessingException
* @since 4.1
*/
public static void addAttachmentsToMimeMessage(MimeMessage msg, List<File> attachments) throws ProcessingException {
if (attachments == null || attachments.isEmpty()) {
return;
}
try {
Object messageContent = msg.getContent();
Multipart multiPart = null;
if (messageContent instanceof Multipart && StringUtility.contains(((Multipart) messageContent).getContentType(), "multipart/mixed")) {
// already contains attachments
// use the existing multipart
multiPart = (Multipart) messageContent;
}
else if (messageContent instanceof Multipart) {
MimeBodyPart multiPartBody = new MimeBodyPart();
multiPartBody.setContent((Multipart) messageContent);
multiPart = new MimeMultipart(); //mixed
msg.setContent(multiPart);
multiPart.addBodyPart(multiPartBody);
}
else if (messageContent instanceof String) {
MimeBodyPart multiPartBody = new MimeBodyPart();
String message = (String) messageContent;
String contentTypeHeader = StringUtility.join(" ", msg.getHeader("Content-Type"));
if (StringUtility.contains(contentTypeHeader, "html")) {
// html
multiPartBody.setContent(message, MailUtility.CONTENT_TYPE_TEXT_HTML);
multiPartBody.setHeader("Content-Type", MailUtility.CONTENT_TYPE_TEXT_HTML);
multiPartBody.setHeader("Content-Transfer-Encoding", "quoted-printable");
}
else {
// plain text
multiPartBody.setText(message);
}
multiPart = new MimeMultipart(); //mixed
msg.setContent(multiPart);
multiPart.addBodyPart(multiPartBody);
}
else {
throw new ProcessingException("Unsupported mime message format. Unable to add attachments.");
}
for (File attachment : attachments) {
MimeBodyPart bodyPart = new MimeBodyPart();
DataSource source = new FileDataSource(attachment);
bodyPart.setDataHandler(new DataHandler(source));
bodyPart.setFileName(MimeUtility.encodeText(attachment.getName(), "UTF-8", null));
multiPart.addBodyPart(bodyPart);
}
msg.saveChanges();
}
catch (MessagingException e) {
throw new ProcessingException("Failed to add attachment to existing mime message", e);
}
catch (IOException e) {
throw new ProcessingException("Failed to add attachment to existing mime message", e);
}
}
/**
* @since 2.7
*/
public static String getContentTypeForExtension(String ext) {
if (ext == null) {
return null;
}
if (ext.startsWith(".")) {
ext = ext.substring(1);
}
ext = ext.toLowerCase();
String type = FileUtility.getContentTypeForExtension(ext);
if (type == null) {
type = MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType("tmp." + ext);
}
return type;
}
/**
* Careful: this method returns null when the list of addresses is empty! This is a (stupid) default by
* javax.mime.Message
*/
private InternetAddress[] parseAddresses(String[] addresses) throws AddressException {
if (addresses == null) {
return null;
}
ArrayList<InternetAddress> addrList = new ArrayList<InternetAddress>();
for (int i = 0; i < Array.getLength(addresses); i++) {
addrList.add(new InternetAddress(addresses[i]));
}
if (addrList.size() == 0) {
return null;
}
else {
return addrList.toArray(new InternetAddress[addrList.size()]);
}
}
private String getCharacterEncodingOfMimePart(MimePart part) throws MessagingException {
Pattern pattern = Pattern.compile("charset=\".*\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
Matcher matcher = pattern.matcher(part.getContentType());
String characterEncoding = "UTF-8"; // default, a good guess in Europe
if (matcher.find()) {
if (matcher.group(0).split("\"").length >= 2) {
characterEncoding = matcher.group(0).split("\"")[1];
}
}
else {
if (part.getContentType().contains("charset=")) {
if (part.getContentType().split("charset=").length == 2) {
characterEncoding = part.getContentType().split("charset=")[1];
}
}
}
return characterEncoding;
}
static {
fixMailcapCommandMap();
}
/**
* jax-ws in jre 1.6.0 and priopr to 1.2.7 breaks support for "Umlaute" ä, ö, ü due to a bug in
* StringDataContentHandler.writeTo
* <p>
* This patch uses reflection to eliminate this buggy mapping from the command map and adds the default text_plain
* mapping (if available, e.g. sun jre)
*/
@SuppressWarnings("unchecked")
private static void fixMailcapCommandMap() {
try {
//set the com.sun.mail.handlers.text_plain to level 0 (programmatic) to prevent others from overriding in level 0
Class textPlainClass;
try {
textPlainClass = Class.forName("com.sun.mail.handlers.text_plain");
}
catch (Throwable t) {
//class not found, cancel
return;
}
CommandMap cmap = MailcapCommandMap.getDefaultCommandMap();
if (!(cmap instanceof MailcapCommandMap)) {
return;
}
((MailcapCommandMap) cmap).addMailcap("text/plain;;x-java-content-handler=" + textPlainClass.getName());
//use reflection to clear out all other mappings of text/plain in level 0
Field f = MailcapCommandMap.class.getDeclaredField("DB");
f.setAccessible(true);
Object[] dbArray = (Object[]) f.get(cmap);
f = Class.forName("com.sun.activation.registries.MailcapFile").getDeclaredField("type_hash");
f.setAccessible(true);
Map<Object, Object> db0 = (Map<Object, Object>) f.get(dbArray[0]);
Map<Object, Object> typeMap = (Map<Object, Object>) db0.get("text/plain");
List<String> handlerList = (List<String>) typeMap.get("content-handler");
//put text_plain in front
handlerList.remove("com.sun.mail.handlers.text_plain");
handlerList.add(0, "com.sun.mail.handlers.text_plain");
}
catch (Throwable t) {
ScoutLogManager.getLogger(MailUtility.class).warn("Failed fixing MailcapComandMap string handling: " + t);
}
}
}