Any alternatives to “JavaMail” for decoding email ...
# cfml-general
j
Any alternatives to “JavaMail” for decoding email MIME in ColdFusion? Preferably a native method.
Context: I’m using AWS SES to receive emails from multiple domains and save them to an S3 bucket. I want to ingest them using ColdFusion and parse them…
w
do you mean like multipart responses?
j
message has been deleted
@websolete Yes, I believe so.
w
yeah ok, so i have something i had to put together to handle a vendor integration where they sent their api response back formatted as if it was email with mime parts (stupid as it sounds). let me remove a couple sensitive things but i'll paste you the function, should give you a starting point at least
runs under cf10 (written for) so should work for anything really
j
Sweet! Thanks.
w
Copy code
/**
	 * Takes a binary multipart/mixed cfhttp result and parses out its parts
	 *
	 * @response struct						the entire result of a cfhttp call
	 * 
	 * @return struct 						struct with each body part set to a key (ofx, pdf (as base64), etc.)
	 * 
	 */	
    public any function parseMultipartHTTPResponse(
        required any response
    ) {

	var parsed = {};

	// these setProperty statements allow for unexpected content formatting that can prevent the multipart processing
	var system = createObject("java", "java.lang.System");
	system.setProperty("mail.mime.multipart.ignoreexistingboundaryparameter", "true");
	system.setProperty("mail.mime.multipart.allowempty", "true");
	
	var dataSource = createObject("java", "javax.mail.util.ByteArrayDataSource").init(arguments.response.fileContent.toByteArray(), javacast("string",arguments.response.responseHeader["content-type"]));
	var mimeParts = createObject("java", "javax.mail.internet.MimeMultipart").init(dataSource);
	
	for ( var i = 0; i < mimeParts.getCount(); i++ ) { 

		try {
    	
			var bp = mimeParts.getBodyPart( javacast("int", i) ); // get next part				
			var tempFilename = "\XYZ_" & gettickcount() & "." & ( bp.isMimeType("application/pdf") ? "pdf" : "xml" ); // we'll need to write to disk as part of this process
			var outputFile = createObject("java", "java.io.File").init( expandPath("/TempFiles") & tempFilename ); // we leverage the native java object method to write the file
			
			bp.saveFile(outputFile); // save the file
			
			if( bp.isMimeType("application/x-ofx") ) { // the xml part of the response
				parsed["ofx"] = fileread( expandPath("/TempFiles") & tempFilename );
			}
			else if( bp.isMimeType("application/pdf") ) { // the pdf part of the response
				var tempPdf = fileReadBinary( expandPath("/TempFiles") & tempFilename );
				parsed["pdf"] = binaryEncode(tempPdf,"base64");
			}
			fileDelete( expandPath("/TempFiles") & tempFilename ); // cleanup

		}
		catch ( any e ) {

			if( fileexists( expandPath("/TempFiles") & tempFilename ) ) {
				fileDelete( expandPath("/TempFiles") & tempFilename ); // cleanup
			}
			rethrow;

		}
			
				
        	}

	return parsed;

    }
so in this case, you're passing it the full http response object, so your mail is probably the equivalent of the filecontent key of that
👍🏻 1
for context, this vendor's api response has an 'ofx' mime part and a pdf mime part
but you can see in my comments that i explicitly write stuff to disk in order to properly leverage the underlying java methods
not sure if that violates the 'no java' requirement you have, but there it is
was a pain in the ass fwiw
j
I just didn’t want a jar that would be obsolete whenever it felt like it. Having visible code is perfect. Thank you! I’ll dig into it tomorrow.
w
np, good luck
b
That example still uses javaX.mail… and I'd get behind that over a homegrown replacement of it any day. Here’s one I did a while back. https://acoderslife.com/posts/default/id/Parsing-email-files-in-ColdFusion-Railo-Lucee.htm
👍🏻 1
a
One question -- why are you generating a temporary filename like that instead of using GetTempFile?
b
Guessing you mean @websolete’s example. But I can’t imagine much benefit to one way or the other.
a
Hmm, maybe it doesn't matter if you're familiar enough with Lucee to know how it's actually working under the hood. I just always assume that if I can do something in native Lucee functions I should start there until there's a reason not to.
w
to answer your question, it was to allow a unique name for the file while also allowing me to specify the file extension. tbh, i've never used getTempFile and have always generated unique/temporary filenames similar to the way i did in the code above. i suppose i could have used getTempFile and replace the .tmp with my own file extension, but i see zero difference with that and how i normally do it. shrug
b
Are you saying you are familiar enough with the inner-workings of Lucee to know it makes enough of a difference to point out THAT specific line of code in that large example? If so, how so?
a
Err, no and I don't know where you got that. I was assuming you knew more about it than I did. Not sure engaging further's useful. Never mind.
b
I didn't get it anywhere, just asked if you knew something I didn't about the intricacies of simple file creation that made you question that specific part of the example code block.
But agreed... back to parsing email.
w
i think it's a productive conversation when we can all just disagree to disagree
b
Makes starting it all worthwhile.
j
@bhartsfield This is sweet! It’s working like 99.9% and saved the attachment. Thanks. :)
b
I just put together a utility for work a few months ago that does the same thing. I based it mostly on the link above but I'll double check it to see if I made any improvements.
w
you keep using that word. i don't think it means what you think it means
b
put?
w
that too, but i meant 'improvements'
b
that said, on occasion the utility does run into some files it can't parse for some reason I haven't figured out yet. I think it may be related to unicode in file names (which are unfortunately derived from the subject in the latest Papercut version)
/imˈpro͞ovmənt/ - The act of removing any code @websolete would use
🤣 1
1
w
in my example there are a couple of setProperty statements that allowed a less strict handling in the java parsing which allowed content that failed minor formatting validation to actually be parsed. there may be some dovetail with the methods you're using
✔️ 1
b
saving your snippet for when I get back to it... you know, just in case you accidentally made something work right.
w
who knows, my code may actually lead to some improvements in yours
b
You can always dream
w
$50 million defamation suit incoming
🤣 1
to be clear, in this scenario i'm johnny depp and you're amber heard
b
@jakobward It looks like there's no real functional difference in that example from 2015 vs the one I created more recently. I mostly just changed up the response to suite the app I was putting it in. If you manage to figure out the %1 failure cause, do let me know 🙂
s
it's only 0.1%
1
b
math is hard
s
it is
b
there are just too many numbers
j
@bhartsfield will do!
w
do the british say 'maths is hards'?
b
probably but with an oy! in there somewhere
s
especially when you start adding in decimals
💯 1
b
ok... I'll stop water-coolering this thread now
j
We could always move the thread to email so I can continue testing…ha!
🤔 1
s
you will need to add some logic to the function that separates the water-cooler comments from the usefull stuff
😆 1
j
ML for the BS.