Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support manually setting content id for attachment #570

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,55 +27,59 @@ public class AttachmentResource implements Serializable {
private static final long serialVersionUID = 1234567L;

/**
* @see #AttachmentResource(String, DataSource, String)
* @see #AttachmentResource(String, String, DataSource, String)
*/
private final String name;

private final String contentId;

/**
* @see #AttachmentResource(String, DataSource, String)
* @see #AttachmentResource(String, String, DataSource, String)
*/
// data source is not serializable, so transient (Kryo can do it though, see SerializationUtil in the OutlookModule)
private transient final DataSource dataSource;

/**
* @see #AttachmentResource(String, DataSource, String)
* @see #AttachmentResource(String, String, DataSource, String)
*/
@Nullable
private final String description;

/**
* @see #AttachmentResource(String, DataSource, String)
* @see #AttachmentResource(String, String, DataSource, String)
*/
@Nullable
private final ContentTransferEncoding contentTransferEncoding;

/**
* Delegates to {@link AttachmentResource#AttachmentResource(String, DataSource, String, ContentTransferEncoding)} with null-description and no forced content transfer encoding
* Delegates to {@link AttachmentResource#AttachmentResource(String, String, DataSource, String, ContentTransferEncoding)} with null-description and no forced content transfer encoding
*/
public AttachmentResource(@Nullable final String name, @NotNull final DataSource dataSource) {
this(name, dataSource, null, null);
public AttachmentResource(@Nullable final String name, final String contentId, @NotNull final DataSource dataSource) {
this(name, contentId, dataSource, null, null);
}

/**
* Delegates to {@link AttachmentResource#AttachmentResource(String, DataSource, String, ContentTransferEncoding)} with no forced content transfer encoding
* Delegates to {@link AttachmentResource#AttachmentResource(String, String, DataSource, String, ContentTransferEncoding)} with no forced content transfer encoding
*/
public AttachmentResource(@Nullable final String name, @NotNull final DataSource dataSource, @Nullable final String description) {
this(name, dataSource, description, null);
public AttachmentResource(@Nullable final String name, final String contentId, @NotNull final DataSource dataSource, @Nullable final String description) {
this(name, contentId, dataSource, description, null);
}

/**
* Constructor; initializes the attachment resource with a name and data.
*
* @param name The name of the attachment which can be a simple name, a filename or a named embedded image (eg. <cid:footer>). Leave
* <code>null</code> to fall back on {@link DataSource#getName()}.
* @param contentId The content id of the attachment. Leave code null if you do not want to set a custom content id.
* @param dataSource The attachment data. If no name was provided, the name of this datasource is used if provided.
* @param description An optional description that will find its way in the MimeMEssage with the Content-Description header. This is rarely needed.
* @param contentTransferEncoding An optional encoder option to force the data encoding while in MimeMessage/EML format.
*
* @see DataSource
*/
public AttachmentResource(@Nullable final String name, @NotNull final DataSource dataSource, @Nullable final String description, @Nullable final ContentTransferEncoding contentTransferEncoding) {
public AttachmentResource(@Nullable final String name, final String contentId, @NotNull final DataSource dataSource, @Nullable final String description, @Nullable final ContentTransferEncoding contentTransferEncoding) {
this.name = name;
this.contentId = contentId;
this.dataSource = checkNonEmptyArgument(dataSource, "dataSource");
this.description = description;
this.contentTransferEncoding = contentTransferEncoding;
Expand Down Expand Up @@ -124,31 +128,39 @@ public InputStream getDataSourceInputStream() {
}

/**
* @see #AttachmentResource(String, DataSource, String)
* @see #AttachmentResource(String, String, DataSource, String)
*/
@NotNull
public DataSource getDataSource() {
return verifyNonnull(dataSource);
}

/**
* @see #AttachmentResource(String, DataSource, String)
* @see #AttachmentResource(String, String, DataSource, String)
*/
@Nullable
public String getName() {
return name;
}

/**
* @see #AttachmentResource(String, DataSource, String)
* @see #AttachmentResource(String, String, DataSource, String)
*/
@Nullable
public String getContentId() {
return contentId;
}

/**
* @see #AttachmentResource(String, String, DataSource, String)
*/
@Nullable
public String getDescription() {
return description;
}

/**
* @see #AttachmentResource(String, DataSource, String)
* @see #AttachmentResource(String, String, DataSource, String)
*/
@Nullable
public ContentTransferEncoding getContentTransferEncoding() {
Expand Down Expand Up @@ -177,6 +189,7 @@ public boolean equals(@Nullable Object o) {
public String toString() {
return "AttachmentResource{" +
"\n\t\tname='" + name + "'" +
",\n\t\tcontentId='" + contentId + "'" +
",\n\t\tdataSource.name=" + dataSource.getName() +
",\n\t\tdataSource.getContentType=" + dataSource.getContentType() +
",\n\t\tdescription=" + (description != null ? "'" + description + "'" : "null") +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1161,11 +1161,21 @@ public interface EmailPopulatingBuilder {
*/
EmailPopulatingBuilder withAttachment(@Nullable String name, byte@NotNull[] data, @NotNull String mimetype);

/**
* Delegates to {@link #withAttachment(String, String, byte[], String, String, ContentTransferEncoding)} with null-description, no forced content transfer encoding and with content id.
*/
EmailPopulatingBuilder withAttachment(@Nullable String name, @NotNull String contentId, byte@NotNull[] data, @NotNull String mimetype);

/**
* Delegates to {@link #withAttachment(String, byte[], String, String, ContentTransferEncoding)} with no forced content transfer encoding.
*/
EmailPopulatingBuilder withAttachment(@Nullable String name, byte@NotNull[] data, @NotNull String mimetype, @Nullable String description);

/**
* Delegates to {@link #withAttachment(String, String, byte[], String, String, ContentTransferEncoding)} with no forced content transfer encoding and forced content id.
*/
EmailPopulatingBuilder withAttachment(@Nullable String name, @NotNull String contentId, byte@NotNull[] data, @NotNull String mimetype, @Nullable String description);

/**
* Delegates to {@link #withAttachment(String, DataSource)}, with a named {@link ByteArrayDataSource} created using the provided name, data and mimetype.
*
Expand All @@ -1181,6 +1191,22 @@ public interface EmailPopulatingBuilder {
*/
EmailPopulatingBuilder withAttachment(@Nullable String name, byte@NotNull[] data, @NotNull String mimetype, @Nullable String description, @Nullable ContentTransferEncoding contentTransferEncoding);

/**
* Delegates to {@link #withAttachment(String, String, DataSource)}, with a named {@link ByteArrayDataSource} created using the provided name, content id and data and mimetype.
*
* @param name Optional name of the attachment (e.g. 'filename.ext'). If omitted, the internal name of the datasource is used. If that too is empty, a name will be generated
* using {@link java.util.UUID}.
* @param contentId The attachment content ID.
* @param data The binary data of the attachment.
* @param mimetype The content type of the given data (e.g. "plain/text", "image/gif" or "application/pdf").
* @param description An optional description that will find its way in the MimeMEssage with the Content-Description header. This is rarely needed.
* @param contentTransferEncoding An optional encoder option to force the data encoding while in MimeMessage/EML format.
*
* @see #withAttachment(String, DataSource, String, ContentTransferEncoding)
* @see #withAttachments(List)
*/
EmailPopulatingBuilder withAttachment(@Nullable String name, @NotNull String contentId, byte@NotNull[] data, @NotNull String mimetype, @Nullable String description, @Nullable ContentTransferEncoding contentTransferEncoding);

/**
* Delegates to {@link #withAttachment(String, DataSource, String, ContentTransferEncoding)} with null-description and no forced content transfer encoding.
*
Expand All @@ -1190,6 +1216,16 @@ public interface EmailPopulatingBuilder {
*/
EmailPopulatingBuilder withAttachment(@Nullable String name, @NotNull DataSource filedata);

/**
* Delegates to {@link #withAttachment(String, String, DataSource, String, ContentTransferEncoding)} with null-description and no forced content transfer encoding.
*
* @param name Optional name of the attachment (e.g. 'filename.ext'). If omitted, the internal name of the datasource is used. If that too is empty, a name will be generated
* using {@link java.util.UUID}.
* @param contentId The attachment content ID.
* @param filedata The attachment data.
*/
EmailPopulatingBuilder withAttachment(@Nullable String name, @NotNull String contentId, @NotNull DataSource filedata);

/**
* Delegates to {@link #withAttachment(String, DataSource, String, ContentTransferEncoding)} with no forced content transfer encoding.
*
Expand All @@ -1201,6 +1237,17 @@ public interface EmailPopulatingBuilder {
@Cli.OptionNameOverride("withDescribedAttachment")
EmailPopulatingBuilder withAttachment(@Nullable String name, @NotNull DataSource filedata, @Nullable final String description);

/**
* Delegates to {@link #withAttachment(String, DataSource, String, ContentTransferEncoding)} with no forced content transfer encoding.
*
* @param name Optional name of the attachment (e.g. 'filename.ext'). If omitted, the internal name of the datasource is used. If that too is empty, a name will be generated
* using {@link java.util.UUID}.
* @param contentId The attachment content ID.
* @param filedata The attachment data.
* @param description An optional description that will find its way in the MimeMEssage with the Content-Description header. This is rarely needed.
*/
EmailPopulatingBuilder withAttachment(@Nullable final String name, @NotNull final String contentId, @NotNull final DataSource filedata, @Nullable final String description);

/**
* Adds an attachment to the email message, which will be shown in the email client as seperate files available for download or inline display if the client supports it (for example, most browsers
* these days display PDF's in a popup).
Expand All @@ -1219,6 +1266,26 @@ public interface EmailPopulatingBuilder {
@Cli.OptionNameOverride("withEncodedDescribedAttachment")
EmailPopulatingBuilder withAttachment(@Nullable String name, @NotNull DataSource filedata, @Nullable final String description, @Nullable final ContentTransferEncoding contentTransferEncoding);


/**
* Adds an attachment to the email message, which will be shown in the email client as seperate files available for download or inline display if the client supports it (for example, most browsers
* these days display PDF's in a popup).
* <p>
* <strong>Note</strong>: for embedding images instead of attaching them for download, refer to {@link #withEmbeddedImage(String, DataSource)} instead.
*
* @param name Optional name of the attachment (e.g. 'filename.ext'). If omitted, the internal name of the datasource is used. If that too is empty, a name will be generated
* using {@link java.util.UUID}.
* @param contentId The attachment content ID.
* @param filedata The attachment data.
* @param description An optional description that will find its way in the MimeMEssage with the Content-Description header. This is rarely needed.
* @param contentTransferEncoding An optional encoder option to force the data encoding while in MimeMessage/EML format.
*
* @see #withAttachment(String, DataSource, String, ContentTransferEncoding)
* @see #withAttachments(List)
*/
@Cli.OptionNameOverride("withEncodedDescribedAttachment")
EmailPopulatingBuilder withAttachment(@Nullable String name, @NotNull final String contentId, @NotNull DataSource filedata, @Nullable final String description, @Nullable final ContentTransferEncoding contentTransferEncoding);

/**
* Delegates to {@link #withAttachment(String, DataSource)} for each attachment.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ private static BodyPart getBodyPartFromDatasource(final AttachmentResource attac
final BodyPart attachmentPart = new MimeBodyPart();
// setting headers isn't working nicely using the javax mail API, so let's do that manually
final String fileName = determineResourceName(attachmentResource, dispositionType, false, false);
final String contentID = determineResourceName(attachmentResource, dispositionType, true, true);
final String contentID = determineResourceContentId(attachmentResource, dispositionType);
attachmentPart.setDataHandler(new DataHandler(new NamedDataSource(fileName, attachmentResource.getDataSource())));
attachmentPart.setFileName(fileName);
final String contentType = attachmentResource.getDataSource().getContentType();
Expand Down Expand Up @@ -303,6 +303,14 @@ static String determineResourceName(final AttachmentResource attachmentResource,
return encodeResourceName ? MiscUtil.encodeText(resourceName) : resourceName;
}

private static String determineResourceContentId(final AttachmentResource attachmentResource, String dispositionType) {
if (dispositionType.equals(Part.ATTACHMENT) && attachmentResource.getContentId() != null) {
return attachmentResource.getContentId();
} else {
return determineResourceName(attachmentResource, dispositionType, true, true);
}
}

@NotNull
private static String possiblyAddExtension(final String datasourceName, String resourceName) {
@SuppressWarnings("UnnecessaryLocalVariable")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1666,7 +1666,7 @@ public EmailPopulatingBuilder withEmbeddedImage(@Nullable final String name, @No
if (valueNullOrEmpty(name) && valueNullOrEmpty(imagedata.getName())) {
throw new EmailException(NAME_MISSING_FOR_EMBEDDED_IMAGE);
}
embeddedImages.add(new AttachmentResource(name, imagedata, null));
embeddedImages.add(new AttachmentResource(name, null, imagedata, null));
return this;
}

Expand Down Expand Up @@ -1742,6 +1742,13 @@ public EmailPopulatingBuilder withHeader(@NotNull final String name, @Nullable f
public EmailPopulatingBuilder withAttachment(@Nullable final String name, final byte@NotNull[] data, @NotNull final String mimetype) {
return withAttachment(name, data, mimetype, null, null);
}
/**
* @see EmailPopulatingBuilder#withAttachment(String, String, byte[], String)
*/
@Override
public EmailPopulatingBuilder withAttachment(@Nullable final String name, @NotNull final String contentId, final byte@NotNull[] data, @NotNull final String mimetype) {
return withAttachment(name, contentId, data, mimetype, null, null);
}

/**
* @see EmailPopulatingBuilder#withAttachment(String, byte[], String, String)
Expand All @@ -1751,6 +1758,14 @@ public EmailPopulatingBuilder withAttachment(@Nullable final String name, final
return withAttachment(name, data, mimetype, description, null);
}

/**
* @see EmailPopulatingBuilder#withAttachment(String, String, byte[], String, String)
*/
@Override
public EmailPopulatingBuilder withAttachment(@Nullable final String name, @NotNull final String contentId, final byte@NotNull[] data, @NotNull final String mimetype, @Nullable final String description) {
return withAttachment(name, contentId, data, mimetype, description, null);
}

/**
* @see EmailPopulatingBuilder#withAttachment(String, byte[], String, String, ContentTransferEncoding)
*/
Expand All @@ -1764,6 +1779,19 @@ public EmailPopulatingBuilder withAttachment(@Nullable final String name, final
return this;
}

/**
* @see EmailPopulatingBuilder#withAttachment(String, String, byte[], String, String, ContentTransferEncoding)
*/
@Override
public EmailPopulatingBuilder withAttachment(@Nullable final String name, @NotNull final String contentId, final byte@NotNull[] data, @NotNull final String mimetype, @Nullable final String description, @Nullable final ContentTransferEncoding contentTransferEncoding) {
requireNonNull(data, "data");
checkNonEmptyArgument(mimetype, "mimetype");
final ByteArrayDataSource dataSource = new ByteArrayDataSource(data, mimetype);
dataSource.setName(name);
withAttachment(name, contentId, dataSource, description, contentTransferEncoding);
return this;
}

/**
* @see EmailPopulatingBuilder#withAttachment(String, DataSource)
*/
Expand All @@ -1772,6 +1800,14 @@ public EmailPopulatingBuilder withAttachment(@Nullable final String name, @NotNu
return withAttachment(name, filedata, null, null);
}

/**
* @see EmailPopulatingBuilder#withAttachment(String, String, DataSource)
*/
@Override
public EmailPopulatingBuilder withAttachment(@Nullable final String name, @NotNull final String contentId, @NotNull final DataSource filedata) {
return withAttachment(name, contentId, filedata, null, null);
}

/**
* @see EmailPopulatingBuilder#withAttachment(String, DataSource, String)
*/
Expand All @@ -1780,23 +1816,47 @@ public EmailPopulatingBuilder withAttachment(@Nullable final String name, @NotNu
return withAttachment(name, filedata, description, null);
}

/**
* @see EmailPopulatingBuilder#withAttachment(String, String, DataSource, String)
*/
@Override
public EmailPopulatingBuilder withAttachment(@Nullable final String name, @NotNull final String contentId, @NotNull final DataSource filedata, @Nullable final String description) {
return withAttachment(name, contentId, filedata, description, null);
}

/**
* @see EmailPopulatingBuilder#withAttachment(String, DataSource, String, ContentTransferEncoding)
*/
@Override
public EmailPopulatingBuilder withAttachment(@Nullable final String name, @NotNull final DataSource filedata, @Nullable final String description, @Nullable final ContentTransferEncoding contentTransferEncoding) {
checkNonEmptyArgument(filedata, "filedata");
attachments.add(new AttachmentResource(name, filedata, description, contentTransferEncoding));
attachments.add(new AttachmentResource(name, null, filedata, description, contentTransferEncoding));
return this;
}

/**
* @see EmailPopulatingBuilder#withAttachment(String, String, DataSource, String, ContentTransferEncoding)
*/
@Override
public EmailPopulatingBuilder withAttachment(@Nullable final String name, @NotNull final String contentId, @NotNull final DataSource filedata, @Nullable final String description, @Nullable final ContentTransferEncoding contentTransferEncoding) {
checkNonEmptyArgument(filedata, "filedata");
attachments.add(new AttachmentResource(name, contentId, filedata, description, contentTransferEncoding));
return this;
}


/**
* @see EmailPopulatingBuilder#withAttachments(List)
*/
@Override
public EmailPopulatingBuilder withAttachments(@NotNull final List<AttachmentResource> attachments) {
for (final AttachmentResource attachment : attachments) {
withAttachment(attachment.getName(), attachment.getDataSource(), attachment.getDescription(), attachment.getContentTransferEncoding());
if (valueNullOrEmpty(attachment.getContentId())) {
withAttachment(attachment.getName(), attachment.getDataSource(), attachment.getDescription(), attachment.getContentTransferEncoding());
}
else {
withAttachment(attachment.getName(), attachment.getContentId(), attachment.getDataSource(), attachment.getDescription(), attachment.getContentTransferEncoding());
}
}
return this;
}
Expand Down
Loading