package org.subethamail.core.injector; import java.io.IOException; import java.io.InputStream; import java.sql.Blob; import java.util.logging.Level; import javax.activation.DataHandler; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.enterprise.context.Dependent; import javax.inject.Inject; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimePart; import javax.mail.util.ByteArrayDataSource; import lombok.extern.java.Log; import org.subethamail.common.MailUtils; import org.subethamail.common.NotFoundException; import org.subethamail.common.SubEthaMessage; import org.subethamail.common.io.TrivialDataSource; import org.subethamail.core.util.BlobImpl; import org.subethamail.core.util.SubEtha; import org.subethamail.core.util.SubEthaEntityManager; import org.subethamail.entity.Attachment; import org.subethamail.entity.Mail; /** * @author Jeff Schnitzer */ @Dependent @TransactionAttribute(TransactionAttributeType.REQUIRED) @Log public class DetacherBean implements Detacher { /** * The was an stream.reset() on the getInputStream method, was this the cause of Issue 44? Is it still needed for a specific reason? */ private static final boolean INPUTSTREAM_RESET_NOT_NEEDED_OR_ISSUE_44 = true; /** */ @Inject @SubEtha SubEthaEntityManager em; /* * (non-Javadoc) * @see org.subethamail.core.injector.Detacher#detach(javax.mail.internet.MimePart, org.subethamail.entity.Mail) */ public void detach(MimePart part, Mail ownerMail) throws MessagingException, IOException { log.log(Level.FINE,"Attempting to detach {0} of type {1}", new Object[]{part, part.getContentType()}); String contentType = part.getContentType().toLowerCase(); if (contentType.startsWith("multipart/")) { log.log(Level.FINE,"Content is multipart"); Multipart multi = (Multipart)part.getContent(); // This is necessary because of the mysterious JavaMail bug 4404733 part.setContent(multi); for (int i=0; i<multi.getCount(); i++) this.detach((MimeBodyPart)multi.getBodyPart(i), ownerMail); } else if (contentType.startsWith("text/") && !Part.ATTACHMENT.equals(part.getDisposition())) { log.log(Level.FINE,"Leaving text alone"); } else { // It would be better if we could do this by knowing which // mime types produce Part Object content = part.getContent(); if (content instanceof MimePart) { log.log(Level.FINE,"Content {0} is part, probably a message", contentType); this.detach((MimePart)content, ownerMail); } else { // Actually detach the sucka log.log(Level.FINE,"Detaching an attachment of type {0}", contentType); InputStream input = part.getInputStream(); Blob blobby = new BlobImpl(input, input.available()); Attachment attach = new Attachment(ownerMail, blobby, contentType); this.em.persist(attach); ownerMail.getAttachments().add(attach); part.setContent(attach.getId(), SubEthaMessage.DETACHMENT_MIME_TYPE); part.setHeader(SubEthaMessage.HDR_ORIGINAL_CONTENT_TYPE, contentType); } } } /* * (non-Javadoc) * @see org.subethamail.core.injector.Detacher#attach(javax.mail.internet.MimePart) */ @SuppressWarnings("deprecation") public void attach(MimePart part) throws MessagingException, IOException { log.log(Level.FINE,"Attempting reattachment for {0} of type {1}", new Object[]{part, part.getContentType()}); String contentType = part.getContentType().toLowerCase(); if (contentType.startsWith("multipart/")) { log.log(Level.FINE,"Content is multipart"); Multipart multi = (Multipart)part.getContent(); // This is necessary because of the mysterious JavaMail bug 4404733 part.setContent(multi); for (int i=0; i<multi.getCount(); i++) this.attach((MimePart)multi.getBodyPart(i)); } else if (contentType.startsWith(SubEthaMessage.DETACHMENT_MIME_TYPE)) { Long attachmentId = (Long)part.getContent(); log.log(Level.FINE,"Reattaching attachment {0} for type {1}", new Object[]{attachmentId, contentType}); try { Attachment att = this.em.get(Attachment.class, attachmentId); if (INPUTSTREAM_RESET_NOT_NEEDED_OR_ISSUE_44) { String ct = att.getContentType(); ByteArrayDataSource bads = new ByteArrayDataSource(att.getContentStream(), ct); //this was lazy, now it always runs. What is the performance impact (~6 string searches/operations)? if (ct!=null) bads.setName(MailUtils.getNameFromContentType(ct)); DataHandler dh = new DataHandler(bads); part.setDataHandler(dh); } else { part.setDataHandler( new DataHandler( new TrivialDataSource( att.getContentStream(), att.getContentType()))); } part.removeHeader(SubEthaMessage.HDR_ORIGINAL_CONTENT_TYPE); } catch (NotFoundException ex) { // Log an error and otherwise leave the mime part as-is. log.log(Level.SEVERE,"Missing referenced attachment {0}", attachmentId); } } else { Object content = part.getContent(); if (content instanceof MimePart) { log.log(Level.FINE,"Content {0} is part, probably a message", contentType ); this.attach((MimePart)content); } else { log.log(Level.FINE,"Ignoring part of type {0}", contentType); } } } }