bbottema/simple-java-mail

Add support for dynamic datasource resolution (file/url/classpath) for embedded images in HTML body

distributev opened this issue · 8 comments

Is there any API available to achieve this?

Currently, in order to embed an image a user would have to modify both the HTML template and the Java code like this

java

currentEmailBuilder.withEmbeddedImage("smiley", new FileDataSource("smiley.jpg"));

and then the corresponding HTML code

<p>Smile!</p><img src='cid:smiley'>

this is fine but in many situations users are allowed to modify the HTML template but the java code would not be available to be modified by the users; thus would be useful to allow dynamically embedding images in emails only by referencing the image files in the HTML code.

I also used the apache commons email and they provide the option, only be modifying the HTML template to include images available both locally and also on remote websites.

Apache commons email has something like a DatasourceResolver which could be either DataSourceFileResolver or DataSourceUrlResolver like this

email.setDataSourceResolver(new DataSourceUrlResolver(new URL("http://website.com")))

email.setDataSourceResolver(new DataSourceFileResolver(new File("./templates")))

and then the user would only be expected to provide the HTML code like this

<img src="template1/images/logo.png"/> - starting from the ./templates parent directory

or from the website

<img src="http://website.com/template1/images/logo.png"/>

Here is the apache commons email API for the resolver

https://commons.apache.org/proper/commons-email/javadocs/api-release/index.html

Is there something similar available in simple-java-mail?

P.S - I believe that simple-java-mail has a very good API and I intend switching from apache commons email for the additional features which your API provides like delivery / read receipt and other things. I also intend to use email-rfc2822-validator for validating email addresses. Thank you for the hard work you put in this project.

Sorry, I see I forgot to reply to this. I think this is a very interesting feature and I'm considering adding it. I'll look into this DataSourceFileResolver.

My first thought is that I would have to parse the HTML for all the src attributes. I solve that (rather ugly) with regular expression.

My first thought is that I would have to parse the HTML for all the src attributes. I solve that (rather ugly) with regular expression.

Looking an commons email, they indeed are using regex to find all images:

    // Regular Expression to find all <IMG SRC="..."> entries in an HTML
    // document.It needs to cater for various things, like more whitespaces
    // including newlines on any place, HTML is not case sensitive and there
    // can be arbitrary text between "IMG" and "SRC" like IDs and other things.

    /** Regexp for extracting {@code <img>} tags */
    public static final String REGEX_IMG_SRC =
            "(<[Ii][Mm][Gg]\\s*[^>]*?\\s+[Ss][Rr][Cc]\\s*=\\s*[\"'])([^\"']+?)([\"'])";

What do you think about the lenient flag Commons Email uses? Lenient means no error will occur for datasources that cannot be resolved, resulting in missing images in the email. Personally, I find that a bad practice, especially because it is a silent process.

I also think that out of the box, if a flag like lenient is not specifically configured, IO Exceptions should be raised that images are missing.

However, if the user will specifically configure lenient as being true, then no exception will be thrown and the emails will be sent with missing images. Warnings could be raised in the log files in this case, but the emails will be sent.

I'm thinking of leaving the flag out completely.

this works too

I'm going all in with this feature, it's a very cool addition!

So here's what I've added and tested fully with junit:

builder
	// enable auto resolution
	.withEmbeddedImageAutoResolutionForFiles(true) // default false
	.withEmbeddedImageAutoResolutionForClassPathResources(true) // default false
	.withEmbeddedImageAutoResolutionForURLs(true) // default false

	// support for base dirs
	.withEmbeddedImageBaseDir(RESOURCES_PATH + "/images")
	.withEmbeddedImageBaseUrl("http://www.simplejavamail.org/static/")
	.withEmbeddedImageBaseClassPath("/images")

	// allow resources outside of basedir (careful, potential security attack surface!)
	.allowingEmbeddedImageOutsideBaseDir(true) // default false
	.allowingEmbeddedImageOutsideBaseClassPath(true) // default false
	.allowingEmbeddedImageOutsideBaseUrl(true) // default false

	// fail if a resource couldn't be resolved
	.embeddedImageAutoResolutionMustBeSuccesful(true) // default false (lenient)

Supported by config properties as well:

simplejavamail.embeddedimages.dynamicresolution.enable.dir=true
simplejavamail.embeddedimages.dynamicresolution.enable.url=true
simplejavamail.embeddedimages.dynamicresolution.enable.classpath=true
simplejavamail.embeddedimages.dynamicresolution.base.dir=...
simplejavamail.embeddedimages.dynamicresolution.base.url=http://www.simplejavamail.org/static/
simplejavamail.embeddedimages.dynamicresolution.base.classpath=/images
simplejavamail.embeddedimages.dynamicresolution.outside.base.dir=true
simplejavamail.embeddedimages.dynamicresolution.outside.base.classpath=true
simplejavamail.embeddedimages.dynamicresolution.outside.base.url=true
simplejavamail.embeddedimages.dynamicresolution.mustbesuccesful=true

Any thoughts on this API design? I think this is more robust than the Commons Email solution.

Feature released in 6.2.0!