Skip to content

Instantly share code, notes, and snippets.

@cflove
Last active February 23, 2021 05:20
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save cflove/4957007 to your computer and use it in GitHub Desktop.
Save cflove/4957007 to your computer and use it in GitHub Desktop.
ColdFusion custom mail tag to use Amazon SES API SendRawEmail call. This is will create Multipart email message and pass into SES API. Mailparam tag can be use to attach file and create custom x-headers slimier to cfmailparam tag.
<cfparam name="attributes.type" default="html">
<cfswitch expression="#thisTag.ExecutionMode#">
<cfcase value="start">
</cfcase>
<cfdefaultcase>
<!--- *************************************************** --->
<!--- Basic Errors --->
<!--- *************************************************** --->
<cfif not StructKeyExists(attributes,'from')>
<cfthrow message="From attribute is required">
</cfif>
<cfif not StructKeyExists(attributes,'to')>
<cfthrow message="TO attribute is required">
</cfif>
<cfif not StructKeyExists(attributes,'Key')>
<cfthrow message="Key attribute is required." detail="Please provide AWS Access Key ID">
</cfif>
<cfif not StructKeyExists(attributes,'SecretKey')>
<cfthrow message="SecretKey attribute is required." detail="Please provide AWS Secret Access Key">
</cfif>
<cfset attributes.boundary = "_#CreateUUID()#_">
<cfset attributes.mail = ArrayNew(1)>
<!--- *************************************************** --->
<!--- Create Header Values --->
<!--- *************************************************** --->
<cfset this.header = ArrayNew(1)>
<cfset ArrayAppend(this.header,"From: #trim(attributes.from)#")>
<cfset ArrayAppend(this.header,"To: #trim(ListChangeDelims(attributes.to,',',',;'))#")>
<cfset attributes.deliverto = attributes.to>
<cfif StructKeyExists(attributes,'cc')>
<cfset ArrayAppend(this.header,"CC: #trim(ListChangeDelims(attributes.cc,',',',;'))#")>
<cfset attributes.deliverto = ListAppend(attributes.deliverto,attributes.cc)>
</cfif>
<cfif StructKeyExists(attributes,'bcc')>
<cfset ArrayAppend(this.header,"BCC: #trim(ListChangeDelims(attributes.bcc,',',',;'))#")>
<cfset attributes.deliverto = ListAppend(attributes.deliverto,attributes.bcc)>
</cfif>
<cfif StructKeyExists(attributes,'replyto')>
<cfset ArrayAppend(this.header,"Reply-To: #trim(attributes.replyto)#")>
</cfif>
<cfif StructKeyExists(attributes,'priority') and val(attributes.priority)>
<cfset ArrayAppend(this.header,"Priority: #attributes.priority#")>
</cfif>
<!--- *************************************************** --->
<!--- Add Custom Header Values --->
<!--- *************************************************** --->
<cfif IsDefined('thisTag.AssocAttribs')>
<cfloop from="1" to="#ArrayLen(thisTag.AssocAttribs)#" index="i">
<cfset this.attr = thisTag.AssocAttribs[i]>
<cfif StructKeyExists(this.attr,'name')>
<cfparam name="this.attr.value" default="">
<cfset ArrayAppend(this.header,"#this.attr.name#: #trim(this.attr.value)#")>
</cfif>
</cfloop>
</cfif>
<cfset ArrayAppend(this.header,"Subject: #trim(attributes.subject)#")>
<cfset ArrayAppend(this.header,"MIME-Version: 1.0")>
<cfset ArrayAppend(this.header,"Content-Type: multipart/mixed; boundary=#attributes.boundary#")>
<cfset ArrayAppend(attributes.mail, ArrayToList(this.header,chr(10)) )>
<cfset ArrayAppend(attributes.mail, '#chr(10)#Your email client does not support MultiPart messages.#chr(10)#' )>
<cfset StructDelete(this,'header')>
<cfswitch expression="#attributes.type#">
<cfcase value="html">
<cfset attributes.type = "text/html">
</cfcase>
<cfdefaultcase>
<cfset attributes.type = "text/plain">
</cfdefaultcase>
</cfswitch>
<!--- *************************************************** --->
<!--- add text content --->
<!--- *************************************************** --->
<cfif len(trim(thisTag.GeneratedContent))>
<cfset ArrayAppend(attributes.mail,'--#attributes.boundary#' )>
<cfset ArrayAppend(attributes.mail,'Content-Type: #attributes.type##chr(10)#')>
<cfset ArrayAppend(attributes.mail,trim(thisTag.GeneratedContent))>
</cfif>
<!--- *************************************************** --->
<!--- add attachments --->
<!--- *************************************************** --->
<cfif IsDefined('thisTag.AssocAttribs')>
<cfloop from="1" to="#ArrayLen(thisTag.AssocAttribs)#" index="i">
<cfset this.attr = thisTag.AssocAttribs[i]>
<cfif StructKeyExists(this.attr,'attachment')>
<cfset ArrayAppend(attributes.mail,'--#attributes.boundary#' )>
<cfset ArrayAppend(attributes.mail, ArrayToList(this.attr.attachment,chr(10)) )>
</cfif>
</cfloop>
</cfif>
<cfset ArrayAppend(attributes.mail,'#chr(10)#--#attributes.boundary#--' )>
<!--- *************************************************** --->
<!--- Remove duplicate email address --->
<!--- *************************************************** --->
<cfset this.deliverto = StructNew()>
<cfloop list="#attributes.deliverto#" index="i" delimiters=",;">
<cfset this.deliverto[trim(i)] = ''>
</cfloop>
<cfset attributes.deliverto = StructKeyList(this.deliverto)>
<!--- *************************************************** --->
<!--- Calling AWS --->
<!--- *************************************************** --->
<cfset this.date = DateAdd('s',GetTimeZoneInfo().UTCTotalOffset,now()) />
<cfset this.date = "#DateFormat(this.date, 'ddd, dd mmm yyyy')# #timeFormat(this.date,'HH:mm:ss')# GMT" />
<cfset this.Signature = toBase64(HMAC_SHA256(this.date,attributes.SecretKey)) />
<cfset this.Authoriz = "AWS3-HTTPS AWSAccessKeyId=#attributes.Key#, Algorithm=HmacSHA256, Signature=#this.Signature#" />
<cfhttp url="https://email.us-east-1.amazonaws.com/" method="post" result="result">
<cfhttpparam type="header" name="X-Amzn-Authorization" value="#this.Authoriz#" />
<cfhttpparam type="header" name="Date" value="#this.date#" />
<cfhttpparam type="formfield" name="Action" value="SendRawEmail" />
<cfset count = 1>
<cfloop list="#attributes.deliverto#" index="i">
<cfhttpparam type="formfield" name="Destinations.members.#count#" value="#i#" />
<cfset count = 1+count>
</cfloop>
<cfhttpparam type="formfield" name="Source" value="#trim(attributes.from)#" />
<cfhttpparam type="formfield" name="RawMessage.Data" value="#ToBase64(trim(ArrayToList(attributes.mail,chr(10))))#" />
</cfhttp>
<cfset this.MessageId = "">
<cfif StructKeyExists(result,'Filecontent') and IsXML(result.Filecontent)>
<cfif listfirst(result.Statuscode,' ') eq '200'>
<cfset this.MessageId = XMLParse(result.Filecontent).SendRawEmailResponse.SendRawEmailResult.MessageId.XmlText>
<cfelse>
<cfset this.MessageId = 'ERROR: '& XMLParse(result.Filecontent).ErrorResponse.Error.Message.XmlText>
</cfif>
</cfif>
<cfset caller.MessageId = trim(this.MessageId)>
<!---- ************************************************************ --->
<!---- Helpers --->
<!---- ************************************************************ --->
<cffunction name="HMAC_SHA256" returntype="binary" access="public" output="no">
<cfargument name="signMessage" type="string" required="true" />
<cfargument name="signKey" type="string" required="true" />
<cfset local.jMsg = JavaCast("string",arguments.signMessage).getBytes("iso-8859-1") />
<cfset local.jKey = JavaCast("string",arguments.signKey).getBytes("iso-8859-1") />
<cfset local.key = createObject("java","javax.crypto.spec.SecretKeySpec") />
<cfset local.mac = createObject("java","javax.crypto.Mac") />
<cfset local.key = local.key.init(local.jKey,"HmacSHA256") />
<cfset local.mac = local.mac.getInstance(local.key.getAlgorithm()) />
<cfset local.mac.init(local.key) />
<cfset local.mac.update(local.jMsg) />
<cfreturn local.mac.doFinal() />
</cffunction>
<cfset thisTag.GeneratedContent = "" />
</cfdefaultcase>
</cfswitch>
<cfparam name="attributes.disposition" default="attachment">
<cfswitch expression="#thisTag.ExecutionMode#">
<cfcase value="start">
<cfassociate basetag="cf_mail">
<cfif StructKeyExists(attributes,'file')>
<!--- *************************************************** --->
<!--- File Attachment --->
<!--- *************************************************** --->
<cfif FileExists(attributes.file)>
<cfset attributes.attachment = ArrayNew(1)>
<cfset this.info = GetFileInfo(attributes.file)>
<cfparam name="attributes.type" default="#getPageContext().getServletContext().getMimeType(attributes.file)#">
<cfset ArrayAppend(attributes.attachment,'Content-Type: #attributes.type#; name="#this.info.name#";')>
<cfset ArrayAppend(attributes.attachment,'Content-Disposition: inline; filename="#this.info.name#"; size=#this.info.size#;')>
<cfif StructKeyExists(attributes,'contentID')>
<cfset ArrayAppend(attributes.attachment,"Content-ID: <#attributes.contentID#>")>
</cfif>
<cfset ArrayAppend(attributes.attachment,"Content-Transfer-Encoding: base64#chr(10)#")>
<cfset ArrayAppend(attributes.attachment,ToBase64( FileReadBinary(attributes.file) ))>
</cfif>
</cfif>
</cfcase>
<cfdefaultcase>
</cfdefaultcase>
</cfswitch>
@WaltRiceJr
Copy link

I forked this and added some preliminary support for cfmailpart and alternative html and plain text content.

@CatStarwind
Copy link

I also forked this to fix a bug that made all cf_mailparam send as disposition: inline.

@SubhanRazaKhan
Copy link

When i am trying to send an email i am getting error "Email address is not verified". I want to send email to my customer but without verification is it possible?

@stitcherooni
Copy link

Do you have any plans to update this to include AWS authorization version 4?
https://gist.github.com/Leigh-/a2798584b79fd9072605a4cc7ff60df4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment