Skip to content

Commit

Permalink
Merge pull request #19 from dulanjalidilmi/master
Browse files Browse the repository at this point in the history
Enable sending inline images in the HTML Content
  • Loading branch information
dulanjalidilmi authored Feb 17, 2023
2 parents 429b8b4 + 193502e commit d739151
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 20 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.wso2.carbon.connector</groupId>
<artifactId>org.wso2.carbon.connector.emailconnector</artifactId>
<version>1.1.1-SNAPSHOT</version>
<version>1.1.1</version>
<packaging>jar</packaging>
<name>WSO2 Carbon - Connector For email</name>
<url>http://wso2.org</url>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ private void sendMessage(MessageContext messageContext, EmailConnection session)
String subject = (String) getParameter(messageContext, EmailConstants.SUBJECT);
String content = (String) getParameter(messageContext, EmailConstants.CONTENT);
String attachments = (String) getParameter(messageContext, EmailConstants.ATTACHMENTS);
String inlineImages = (String) getParameter(messageContext, EmailConstants.INLINE_IMAGES);
String contentType = (String) getParameter(messageContext, EmailConstants.CONTENT_TYPE);
String encoding = (String) getParameter(messageContext, EmailConstants.ENCODING);
String contentTransferEncoding = (String) getParameter(messageContext, EmailConstants.CONTENT_TRANSFER_ENCODING);
Expand All @@ -103,6 +104,7 @@ private void sendMessage(MessageContext messageContext, EmailConnection session)
.replyTo(replyTo)
.withSubject(subject)
.withBody(content, contentType, encoding, contentTransferEncoding)
.withInlineImages(inlineImages)
.withAttachments(attachments)
.withHeaders(getEmailHeadersFromProperties(messageContext))
.build();
Expand All @@ -114,7 +116,7 @@ private void sendMessage(MessageContext messageContext, EmailConnection session)
throw new EmailConnectionException(
format("Error occurred while sending the email with subject %s to %s.", subject, to), e);
} catch (IOException e) {
throw new EmailConnectionException(format("Error occurred while adding attachments with subject %s " +
throw new EmailConnectionException(format("Error occurred while building MIME message with subject %s " +
"to the email to %s.", subject, to), e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public final class EmailConstants {
public static final String CONTENT_TYPE = "contentType";
public static final String ENCODING = "encoding";
public static final String ATTACHMENTS = "attachments";
public static final String INLINE_IMAGES = "inlineImages";
public static final String CONTENT_TRANSFER_ENCODING = "contentTransferEncoding";
public static final String CONNECTION_TYPE = "connectionType";
public static final String HOST = "host";
Expand Down
137 changes: 119 additions & 18 deletions src/main/java/org/wso2/carbon/connector/utils/MessageBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,36 @@
package org.wso2.carbon.connector.utils;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.tika.Tika;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.wso2.carbon.connector.exception.InvalidConfigurationException;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;
import java.util.regex.Pattern;
import javax.activation.DataHandler;

import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
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.util.ByteArrayDataSource;

import static javax.mail.Part.ATTACHMENT;
import static javax.mail.Part.INLINE;
Expand All @@ -56,11 +64,16 @@ public final class MessageBuilder {
private static final String DEFAULT_ENCODING = "UTF-8";
private static final String DEFAULT_CONTENT_TRANSFER_ENCODING = "Base64";
private static final String REGEX = "([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)";
public static final String FILE_PATH = "filePath";
public static final String BASE64_CONTENT = "base64Content";
public static final String CONTENT_ID = "contentID";
public static final String FILE_NAME = "fileName";
private static final Pattern pattern = Pattern.compile(REGEX);

private final MimeMessage message;

private String attachments;
private String inlineImages;
private String contentType;
private String contentTransferEncoding;
private String content;
Expand Down Expand Up @@ -237,34 +250,36 @@ public MessageBuilder withAttachments(String attachments) {
return this;
}

/**
* Add inline images to the email
*
* @param inlineImages Inline image details to be embedded in the email
* @return MessageBuilder instance
*/
public MessageBuilder withInlineImages(String inlineImages) {
if (StringUtils.isNotEmpty(inlineImages)) {
this.inlineImages = inlineImages;
}
return this;
}

/**
* Build MIME Message using configured parameters
*
* @return MIME Message with the configured values
* @throws MessagingException if failed to build message
*/
public MimeMessage build() throws MessagingException, IOException {

if (attachments != null && !attachments.isEmpty()) {
public MimeMessage build() throws MessagingException, IOException, InvalidConfigurationException {
if (StringUtils.isNotEmpty(attachments) || StringUtils.isNotEmpty(inlineImages)) {
MimeMultipart multipart = new MimeMultipart();
MimeBodyPart body = new MimeBodyPart();
setMessageContent(body);
multipart.addBodyPart(body);
//check whether the attachment is a json array.
if (attachments.startsWith("[")) {
try {
JSONArray attachmentsArray = new JSONArray(attachments);
for (int i = 0; i < attachmentsArray.length(); i++) {
addAttachment(multipart, attachmentsArray.getJSONObject(i));
}
} catch (JSONException e) {
throw new IOException("Invalid JSON data format.", e);
}
} else {
String[] attachFiles = attachments.split(",");
for (String filePath : attachFiles) {
addAttachment(multipart, filePath);
}
if (StringUtils.isNotEmpty(attachments)) {
buildMultipartMessageWithAttachments(multipart);
}
if (StringUtils.isNotEmpty(inlineImages)) {
buildMultipartMessageWithInlineImages(multipart);
}
message.setContent(multipart, MULTIPART_TYPE);
} else {
Expand All @@ -273,6 +288,92 @@ public MimeMessage build() throws MessagingException, IOException {
return message;
}

private void buildMultipartMessageWithInlineImages(MimeMultipart multipart)
throws MessagingException, IOException, InvalidConfigurationException {
try {
JSONArray inlineImagesArray = new JSONArray(inlineImages);
for (int i = 0; i < inlineImagesArray.length(); i++) {
BodyPart imagePart = getInlineImage(inlineImagesArray.getJSONObject(i));
multipart.addBodyPart(imagePart);
}
} catch (JSONException e) {
throw new IOException("Invalid JSON data format for inline images.", e);
}
}

private void buildMultipartMessageWithAttachments(MimeMultipart multipart) throws MessagingException, IOException {
if (attachments.startsWith("[")) {
try {
JSONArray attachmentsArray = new JSONArray(attachments);
for (int i = 0; i < attachmentsArray.length(); i++) {
addAttachment(multipart, attachmentsArray.getJSONObject(i));
}
} catch (JSONException e) {
throw new IOException("Invalid JSON data format for attachments.", e);
}
} else {
String[] attachFiles = attachments.split(",");
for (String filePath : attachFiles) {
addAttachment(multipart, filePath);
}
}
}

private BodyPart getInlineImage(JSONObject inlineImage)
throws IOException, InvalidConfigurationException, MessagingException {
if (inlineImage.has(FILE_PATH)) {
return getImagePartFromFilePath(inlineImage);
} else if (inlineImage.has(BASE64_CONTENT)) {
return getImagePartFromBase64(inlineImage);
} else {
throw new InvalidConfigurationException(
"Mandatory parameters 'filePath' or 'base64Content' are not set for inline image.");
}
}

private BodyPart getImagePartFromBase64(JSONObject inlineImage)
throws InvalidConfigurationException, MessagingException {
BodyPart imagePart = new MimeBodyPart();
try {
String base64Content = inlineImage.get(BASE64_CONTENT).toString();
String contentId = inlineImage.get(CONTENT_ID).toString();
String fileName = inlineImage.get(FILE_NAME).toString();

byte[] rawImage = Base64.decodeBase64(base64Content.getBytes());
String imageType = "image/".concat(FilenameUtils.getExtension(fileName));
ByteArrayDataSource imageDataSource = new ByteArrayDataSource(rawImage, imageType);

imagePart.setDataHandler(new DataHandler(imageDataSource));
imagePart.setHeader("Content-ID", "<".concat(contentId).concat(">"));
imagePart.setFileName(fileName);
return imagePart;
} catch (JSONException e) {
throw new InvalidConfigurationException("Invalid JSON data format for inline image.", e);
}
}

private BodyPart getImagePartFromFilePath(JSONObject inlineImage)
throws InvalidConfigurationException, IOException, MessagingException {
BodyPart imagePart = new MimeBodyPart();
try {
String filePath = inlineImage.get(FILE_PATH).toString();
String contentId = inlineImage.get(CONTENT_ID).toString();

byte[] fileContent = Files.readAllBytes(Paths.get(filePath));
String imageType = "image/".concat(FilenameUtils.getExtension(filePath));
ByteArrayDataSource imageDataSource = new ByteArrayDataSource(fileContent, imageType);

imagePart.setDataHandler(new DataHandler(imageDataSource));
imagePart.setHeader("Content-ID", "<".concat(contentId).concat(">"));

String fileName = FilenameUtils.getName(filePath);
imagePart.setFileName(fileName);
return imagePart;
} catch (JSONException e) {
throw new InvalidConfigurationException("Invalid JSON data format for inline image.", e);
}
}

/**
* Sets message content in message body
*
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/functions/send.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<parameter name="contentType" description="Content Type of the body text"/>
<parameter name="encoding" description="The character encoding of the body"/>
<parameter name="attachments" description="The attachments that are sent along with the email body"/>
<parameter name="inlineImages" description="The inline images that should be embedded inline to email body"/>
<parameter name="contentTransferEncoding" description="Encoding used to indicate the type of transformation that is used to represent the body in an acceptable manner for transport"/>
<sequence>
<property name="from" expression="$func:from"/>
Expand All @@ -39,6 +40,7 @@
<property name="contentType" expression="$func:contentType"/>
<property name="encoding" expression="$func:encoding"/>
<property name="attachments" expression="$func:attachments"/>
<property name="inlineImages" expression="$func:inlineImages"/>
<property name="contentTransferEncoding" expression="$func:contentTransferEncoding"/>
<class name="org.wso2.carbon.connector.operations.EmailSend" />
</sequence>
Expand Down
11 changes: 11 additions & 0 deletions src/main/resources/uischema/send.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@
"required": "false",
"helpTip": "Comma separated file paths of the attachments that are sent along with the email body"
}
},
{
"type": "attribute",
"value": {
"name": "inlineImages",
"displayName": "Inline Images",
"inputType": "stringOrExpression",
"defaultValue": "[]",
"required": "false",
"helpTip": "Array of comma separated inline images that should be embedded inline to email body"
}
}
]
}
Expand Down

0 comments on commit d739151

Please sign in to comment.