Cleaning Up And Sending One-Off Undelivered ColdFusion Emails

When I'm developing a ColdFusion application on my local development environment, I'll usually define invalid SMTP (Simple Mail Transfer Protocol) credentials so that the CFMail tag doesn't actually send out mail. Instead, ColdFusion ends up dumping the outgoing mail items into the "Undelivr" folder. Then, I'll use the "Undelivered Mail" module in the ColdFusion administrator to view these undelivered mail items. Unfortunately, this approach provides a somewhat skewed representation of how the outgoing mail items will actual render in email clients. As such, I like to occasionally take one of the undelivered mails items and send it out to my various email inboxes.

Rather than temporarily changing out the SMTP credentials in the ColdFusion administrator (or in my per-Application SMTP server settings), I simply grab the most recent undelivered email item, clean it up, and then send it out using new credentials. While this concept is simple, cleaning up the ".cfmail" file content requires some Regular Expression replacement; as such, I thought I'd share my little stand-alone, one-off sender.

<cfscript>

// Query for the most recently undelivered mail definition. Since

// we can't actually limit to a single file, we will order it

// based on date file was created - newest ones first.

// --

// NOTE: The location of the undelivered mail folder (Undelivr)

// will depend on your installation and ColdFusion configuration.

mailItems = directoryList(

"/Applications/ColdFusion10/cfusion/Mail/Undelivr/",

false,

"query",

"*.cfmail",

"dateLastModified DESC"

);

// If there is no mail, just exit.

if ( ! mailItems.recordCount ) {

writeOutput( "No undelivered mail found." );

exit;

}

// Read in the content of the most recent undelivered mail item.

// Note that this is NOT the raw content of the CFMail tag; rather,

// it contains additional meta data about the mail. The content

// will look somethign like this:

// --

// type: text/html

// server: ...

// from: ...

// to: ...

// replyto: ...

// subject: ...

// X-Mailer: ...

// body: This is some mail content.

// body: This is some mail content.

// body: This is some mail content.

content = fileRead( "#mailItems.directory#/#mailItems.name#" );

// Find the type of mail content (HTML vs. Plain). If we don't

// find "text/html", we're going to assume Plain text.

isHtml = !! reFind( "(?mi)^type:\s*text/html", content );

// Strip out all the meta-data before the mail content.

content = javaCast( "string", content ).replaceAll(

javaCast( "string", "(?mi)^(?!body:)[^\r\n]*[\r\n]" ),

javaCast( "string", "" )

);

// Strip out all the per-line "body:" content markers.

content = javaCast( "string", content ).replaceAll(

javaCast( "string", "(?mi)^body:\s*" ),

javaCast( "string", "" )

);

// Send out the mail using LIVE mail server credentials.

new Mail().send(

to = "ben@bennadel.com",

from = "ben@bennadel.com",

subject = "TEST EMAIL",

body = content,

type = ( isHtml ? "html" : "plain" ),

spoolEnable = false,

server = "************",

port = "****",

username = "************",

password = "************"

);

writeOutput( "Mail Sent, check your inbox!" );

</cfscript>

This ColdFusion script strips out the pre-content metadata as well as the "body:" content markers. It then creates a new CFMail tag (via CFScript) and re-sends the email using valid credentials. It's simple, but it has been a huge time-saver for testing the rendering quirks in various email clients while maintaining a siloed development environment.

Good questions. It's intentional. The !! "operator" converts the reFind() result to a more "correct" boolean value. Since reFind() returns the index of the match within the string, the "!!" converts it from a number to "True."

Imagine that the reFind() result was 18. Then:

! 18 => False!! 18 => ! False => True

It's not really necessary since ColdFusion treats any non-Zero number as a Truthy. It's just something I've picked up in the JavaScript world.

I've generally taken the approach of a function, or you could make a custom tag, that handles cfmail calls. Within the function I do a check for production vs non-production. If it's not the production server I replace the TO field with the currently logged in users e-mail and get rid of the CC field.

I then append to the e-mail something of "This e-mail was sent to you because it was not triggered in production. In production this would normally go to the following:"

There I'd have it display the TO and CC values and then right after that just have an <hr> tag or something. This lets me test everything and also ensure e-mails are also going around where they should be as I'm going through tests without having to modify any code or mess with the mail messages. This e-mail function is also where I let myself use sort of quick variables of like [webUrl] which will get replaced with the valid web URL for whichever environment I'm in and other such things.

That's an interesting approach. Though, it seems like you would really need to plan ahead from something like that. One thing that I really enjoy about my approach is that it doesn't actually require any updates to the App code itself.

Right, but how do you actually make sure that emails get delivered to You as opposed to an intended user. For example, imagine that I had to send some email to a user, "ben@example.com". Well, if I have no SMTP setup, then I don't have to care whether or not "example.com" actually exists. But, if I DO have a valid SMTP setup, then I have to be careful.

And, I think what you're saying is that if you're on the local machine, you make sure to set the "TO" address to be a single user, rather than the user defined in the business logic?

I discover today in a V2 update for The Jungle that if you neglect to apply the rules - you "The quick brown fox jumped over the lazy dog" soon gets an official response from the help desk software. Doh!

For an update to Bootstrap 3 and the latest in icons from AwesomeFonts. Worth it.

My point is in development anything going out comes to me. I become the CC. Yes I know about BCC bane of any developer.

The result is immediate and I am the TO recipient rather than my companies system help desk - at least under development.

How many times they ring you back "is this real" hence the Lazy Dog. These are existing systems. So to test out of the circle is a good thing. When the site goes LIVE you hope that your code is up to it.

"Test message please ignore" can avoid it mostly until site goes LIVE then you cannot help yourself to a) be the first or b) test.

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments

Live in the Now

Oops!

Name:

Email:

( I keep this private )

Website:

Comment:

Subscribe to comments.

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please
do not post unrelated questions or
large chunks of code. And, above all, please be nice to each other - we're trying to
have a good conversation here.

I am the co-founder and lead engineer at InVision App, Inc — the world's leading prototyping,
collaboration & workflow platform. I also rock out in JavaScript and ColdFusion 24x7 and I dream about
promise resolving asynchronously.