diff --git a/api/src/main/java/org/sakaiproject/email/api/Attachment.java b/api/src/main/java/org/sakaiproject/email/api/Attachment.java
index 79e8653..1006b7f 100644
--- a/api/src/main/java/org/sakaiproject/email/api/Attachment.java
+++ b/api/src/main/java/org/sakaiproject/email/api/Attachment.java
@@ -17,42 +17,94 @@
package org.sakaiproject.email.api;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
/**
* Holds an attachment for an email message. The attachment will be included with the message.
*
- * TODO: Make available for attachments to be stored in CHS.
- *
+ * @see javax.activation.DataSource
+ * @see javax.activation.FileDataSource
* @author Carl Hall
*/
public class Attachment
{
/**
- * files to associated to this attachment
+ * file to associated to this attachment
+ */
+ private final DataSource dataSource;
+
+ /**
+ * The Content-Type and Content-Disposition MIME headers to be sent with the attachment.
+ * Can be null
.
+ */
+ private final String contentDisposition;
+ private final String contentType;
+
+ public enum ContentDisposition {INLINE, ATTACHMENT}
+
+ /**
+ * Creates an Attachment with some of the MIME headers specified.
+ *
+ * @param dataSource the data source
+ * @param contentType the Content-Type header, can be null
+ * @param disposition the Content-Disposition header, can be null
+ */
+ public Attachment(DataSource dataSource, String contentType, ContentDisposition disposition)
+ {
+ this.dataSource = dataSource;
+ this.contentType = contentType;
+ this.contentDisposition = disposition == null ? null : disposition.toString().toLowerCase();
+ }
+
+ /**
+ * Creates an Attachment.
+ *
+ * @param dataSource The data source to use for the attachment.
*/
- private DataSource dataSource;
- private final String filename;
+ public Attachment(DataSource dataSource)
+ {
+ this(dataSource, null, null);
+ }
+ /**
+ * Creates a attachment based on a file.
+ * @param file The file to load the contents of the attachement from and to get the mimetype
+ * from.
+ * @param filename The filename to call the attachment when sent out, doesn't have to match
+ * the file from which the content is loaded.
+ * @deprecated {@link org.sakaiproject.email.api.Attachment#Attachment(javax.activation.DataSource)}
+ */
public Attachment(File file, String filename)
{
- this(new FileDataSource(file),filename);
+ this(new RenamedDataSource(new FileDataSource(file), filename));
}
- public Attachment(DataSource dataSource,String filename) {
- this.dataSource = dataSource;
- this.filename = filename;
+
+ /**
+ * Creates an attachment supplying an different filename.
+ * @param dataSource The data source.
+ * @param filename The alternative filename.
+ */
+ public Attachment(DataSource dataSource, String filename)
+ {
+ this(new RenamedDataSource(dataSource, filename), null, null);
}
+
/**
* Get the file associated to this attachment
*
- * @return file associated with this attachment (created from the DataSource)
+ * @return file associated with this attachment (created from the DataSource) or null
if there
+ * isn't an underlying file for this attachment.
+ * @deprecated As not all Attachments will have an underlying file this method shouldn't be used.
*/
public File getFile()
{
- return ((FileDataSource)dataSource).getFile();
+ return (dataSource instanceof FileDataSource)?((FileDataSource)dataSource).getFile():null;
}
/**
@@ -62,17 +114,73 @@ public class Attachment
*/
public String getFilename()
{
- return filename;
+ return dataSource.getName();
}
-
+
+ /**
+ * The Content-Type MIME header for the attachment, can be null
.
+ * This does not return the mime type of the data source as you may wish to supply a different one
+ * or no content-type at all.
+ *
+ * @return the Content-Type header
+ */
+ public String getContentTypeHeader()
+ {
+ return contentType;
+ }
+
/**
* Get the datasource of the attachment
*
* @return datasource of the attachment
*/
- public DataSource getDataSource()
- {
- return dataSource;
+ public DataSource getDataSource()
+ {
+ return dataSource;
}
-}
\ No newline at end of file
+ /**
+ * The Content-Disposition MIME header for the attachment, can be null
.
+ *
+ * @return the Content-Disposition header
+ */
+ public String getContentDispositionHeader()
+ {
+ return contentDisposition;
+ }
+
+ /**
+ * Class which can be used when you wish to rename a file when adding it as an attachment.
+ * All calls are passed through to the original data source apart from the name.
+ */
+ public static class RenamedDataSource implements DataSource {
+
+ private final DataSource dataSource;
+ private final String name;
+
+ public RenamedDataSource(DataSource dataSource, String name) {
+ this.dataSource = dataSource;
+ this.name = name;
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return dataSource.getInputStream();
+ }
+
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ return dataSource.getOutputStream();
+ }
+
+ @Override
+ public String getContentType() {
+ return dataSource.getContentType();
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+ }
+}
diff --git a/api/src/main/java/org/sakaiproject/email/api/EmailHeaders.java b/api/src/main/java/org/sakaiproject/email/api/EmailHeaders.java
index c16e792..33fa88d 100644
--- a/api/src/main/java/org/sakaiproject/email/api/EmailHeaders.java
+++ b/api/src/main/java/org/sakaiproject/email/api/EmailHeaders.java
@@ -21,4 +21,5 @@ public interface EmailHeaders
String IN_REPLY_TO = "In-Reply-To";
String LIST_ID = "List-Id";
String MESSAGE_ID = "Message-Id";
+ String MULTIPART_SUBTYPE = "Multipart-Subtype";
}
\ No newline at end of file
diff --git a/kernel-impl/src/main/java/org/sakaiproject/email/impl/BasicEmailService.java b/kernel-impl/src/main/java/org/sakaiproject/email/impl/BasicEmailService.java
index ecbd05a..3645419 100644
--- a/kernel-impl/src/main/java/org/sakaiproject/email/impl/BasicEmailService.java
+++ b/kernel-impl/src/main/java/org/sakaiproject/email/impl/BasicEmailService.java
@@ -507,6 +507,11 @@ public class BasicEmailService implements EmailService
// Content-Type: text/plain; charset=windows-1252; format=flowed
String contentTypeHeader = null;
+ // If we need to force the container to use a certain multipart subtype
+ // e.g. 'alternative'
+ // then sneak it through in the additionalHeaders
+ String multipartSubtype = null;
+
// set the additional headers on the message
// but treat Content-Type specially as we need to check the charset
// and we already dealt with the message id
@@ -516,6 +521,8 @@ public class BasicEmailService implements EmailService
{
if (header.toLowerCase().startsWith(EmailHeaders.CONTENT_TYPE.toLowerCase() + ": "))
contentTypeHeader = header;
+ else if (header.toLowerCase().startsWith(EmailHeaders.MULTIPART_SUBTYPE.toLowerCase() + ": "))
+ multipartSubtype = header.substring(header.indexOf(":") + 1).trim();
else if (!header.toLowerCase().startsWith(EmailHeaders.MESSAGE_ID.toLowerCase() + ": "))
msg.addHeaderLine(header);
}
@@ -588,7 +595,7 @@ public class BasicEmailService implements EmailService
int colonPos = contentTypeHeader.indexOf(":");
contentType = contentTypeHeader.substring(colonPos + 1).trim();
}
- setContent(content, attachments, msg, contentType, charset);
+ setContent(content, attachments, msg, contentType, charset, multipartSubtype);
// if we have a full Content-Type header, set it NOW
// (after setting the body of the message so that format=flowed is preserved)
@@ -1198,15 +1205,10 @@ public class BasicEmailService implements EmailService
/**
* Sets the content for a message. Also attaches files to the message.
- *
- * @param content
- * @param attachments
- * @param msg
- * @param charset
* @throws MessagingException
*/
protected void setContent(String content, List attachments, MimeMessage msg,
- String contentType, String charset) throws MessagingException
+ String contentType, String charset, String multipartSubtype) throws MessagingException
{
ArrayList embeddedAttachments = new ArrayList();
if (attachments != null && attachments.size() > 0)
@@ -1232,7 +1234,7 @@ public class BasicEmailService implements EmailService
else
{
// create a multipart container
- Multipart multipart = new MimeMultipart();
+ Multipart multipart = (multipartSubtype != null) ? new MimeMultipart(multipartSubtype) : new MimeMultipart();
// create a body part for the message text
MimeBodyPart msgBodyPart = new MimeBodyPart();
@@ -1257,8 +1259,7 @@ public class BasicEmailService implements EmailService
/**
* Attaches a file as a body part to the multipart message
- *
- * @param multipart
+ *
* @param attachment
* @throws MessagingException
*/
@@ -1266,8 +1267,18 @@ public class BasicEmailService implements EmailService
{
DataSource source = attachment.getDataSource();
MimeBodyPart attachPart = new MimeBodyPart();
+
attachPart.setDataHandler(new DataHandler(source));
attachPart.setFileName(attachment.getFilename());
+
+ if (attachment.getContentTypeHeader() != null) {
+ attachPart.setHeader("Content-Type", attachment.getContentTypeHeader());
+ }
+
+ if (attachment.getContentDispositionHeader() != null) {
+ attachPart.setHeader("Content-Disposition", attachment.getContentDispositionHeader());
+ }
+
return attachPart;
}