cf_sesmail custom tag for Railo
Lately I have been using Amazon’s Simple Email Service quite a bit. I use the AmazonSES component for a few clients, but it still is not quite as natural as using a cfmail tag. Running PostFix to relay messages through SES is not hard to setup, but it is a little overkill if you just need to use it from ColdFusion. With these things in mind, I remembered the post Todd Rafferty wrote about writing a cfc custom tag. I thought it would be pretty cool to write a custom tag to allow you to send an email through SES with the familiar feel of cfmail. If you would like to check it out, it is pretty easy to setup.
sesmail.cfc
component {
/*
* Project: sesmail
* Author : Robert Zehnder
* Date : 9/2/2011
* Purpose: cfmail-like implementation to make it easier to send emails through Amazon Simple Email Service
*/
this.metaData.attributeType = "fixed";
this.metaData.attributes = {
from : { required: true, type: "string" },
to : { required: true, type: "string" },
cc : { required: false, type: "string", default: "" },
bcc : { required: false, type: "string", default: "" },
subject : { required: true, type: "string" },
mailerID : { required: false, type: "string", default: "cfmailses" },
endPoint : { required: false, type: "string", default: "" },
credentials : { required: true, type: "string" },
name : { required: false, type: "string", default: "sesResults" }
};
public void function init(required boolean hasEndTag, any parent) {
}
public boolean function onStartTag(struct attributes, struct caller) {
return true;
}
public boolean function onEndTag(struct attributes, struct caller) {
var results = {};
var awsCredentials = createObject("java", "java.io.File").init(attributes.credentials);
var creds = createObject("java", "com.amazonaws.auth.PropertiesCredentials").init(awsCredentials);
var emailService = createObject("java", "com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient").init(creds);
var props = createObject("java", "java.util.Properties");
var verifyRequest = createObject("java", "com.amazonaws.services.simpleemail.model.VerifyEmailAddressRequest").withEmailAddress(attributes.from);
var sendHeaders = { "User-Agent" : attributes.mailerID };
// Set properties for establishing connection
props.setProperty("mail.transport.protocol", "aws");
props.setProperty("mail.aws.user", creds.getAWSAccessKeyId());
props.setProperty("mail.aws.password", creds.getAWSSecretKey());
// Send email message
var mailSession = createObject("java", "javax.mail.Session").getInstance(props);
var mailTransport = createObject("java", "com.amazonaws.services.simpleemail.AWSJavaMailTransport").init(mailSession, JavaCast("null", 0));
var messageObj = createObject("java", "javax.mail.internet.MimeMessage").init(mailSession);
var messageRecipientType = createObject("java", "javax.mail.Message$RecipientType");
var messageFrom = createObject("java", "javax.mail.internet.InternetAddress").init(attributes.from);
var messageTo = listToArray(attributes.to);
var messageCC = listToArray(attributes.cc);
var messageBCC = listToArray(attributes.bcc);
var messageSubject = attributes.subject;
var messageBody = arguments.generatedContent;
var verified = arrayToList(emailService.ListVerifiedEmailAddresses().getVerifiedEmailAddresses()).contains(attributes.from);
var i = 0;
try {
// Is the sender verified
if(!verified){
var verifyRequest = createObject("java", "com.amazonaws.services.simpleemail.model.VerifyEmailAddressRequest").withEmailAddress(attributes.from);
try{
emailService.verifyEmailAddress(verifyRequest);
}
catch (any e){
}
throw("Email address has not been validated. Please check the email on account " & attributes.from & " to complete validation.");
}
mailTransport.connect();
messageObj.setFrom(messageFrom);
for(i = 1; i <= arrayLen(messageTo); i++){
messageObj.addRecipient(messageRecipientType.TO, createObject("java", "javax.mail.internet.InternetAddress").init(trim(messageTo[i])));
}
if(arrayLen(messageCC)){
for(i = 1; i <= arrayLen(messageCC); i++){
messageObj.addRecipient(messageRecipientType.CC, createObject("java", "javax.mail.internet.InternetAddress").init(trim(messageCC[i])));
}
}
if(arrayLen(messageBCC)){
for(i = 1; i <= arrayLen(messageBCC); i++){
messageObj.addRecipient(messageRecipientType.BCC, createObject("java", "javax.mail.internet.InternetAddress").init(trim(messageBCC[i])));
}
}
if(len(structKeyList(sendHeaders))){
for(i in sendHeaders){
messageObj.addHeader(i, sendHeaders[i]);
}
}
messageObj.setSubject(messageSubject);
messageObj.setContent(messageBody, "text/html");
messageObj.saveChanges();
mailTransport.sendMessage(messageObj, JavaCast("null", 0));
mailTransport.close();
}
catch (Any e){
throw("Error sending message.");
}
return false;
}
}
Now we can send mail through the SES gateway like this:
<cf_sesmail from="user@domain" to="other@domain" subject="subject" credentials="/path/to/awscredentials.properties"> Hello from cf_sesmail! </cf_sesmail>
It is self-explanatory. You supply the basics such as the from address, to address and subject. Instead of passing a user name and password for authentication, you pass the full path to your awscredentials.properties file and the tag takes care of the rest. The code is still a little rough around the edges but it is usable. I will get something up on github soon.
Here is a link to the version of the AWS SDK I am using for development. It is a little dated, but it works perfectly with Railo without requiring you to update any additional jars in your installation. Just extact this file and place the sdk jar into your Railo classpath. For Linux systems, this will most likely be /opt/railo/lib and you should be good to go. If you want to really integrate it into your web context you can copy the component into your WEB-INF/railo/library/tags/ folder, this will allow you to use the custom tag just as if it was a built in function (i.e., <cfsesmail ..></cfsesmail>).
Updated 9/4/2011:
Project is now hosted on github here: https://github.com/robertz/cf_sesmail
Recent Comments