Sending Emails From LiveCode Server Scripts

A common task among web scripts is the sending of emails, be it for verifying user accounts or confirming online orders.  This lesson covers the sending of emails from LiveCode Server scripts.

Note: This lesson only applies on OSX and Linux servers.

Calling Sendmail From LiveCode

The easiest way to send emails from a LiveCode Server script is to shell out to a command line application.  In this example, we will use sendmail.  Sendmail can be called from the command line in the following manner:

[user@user: /]$ sendmail to@address.com -f from@address.com

Users can then type in their message subject and body.  We will create a LiveCode command that will wrap sendmail, calling it via the shell function, allowing users to send emails from LiveCode Server scripts.  Our command will have the following prototype:

-- mail

--

-- Emails the given message to the recipients specified.

-- Each address passed can have a name  attached in the form "name <address>".

-- Addresses can be passed as comma separated lists.

-- Attachements can be added by passing an array (interger indexed or otherwise).

-- with each attachment itself being an array.

--

-- pTo - The addresses to send the message to

-- pSub - The message subject

-- pMsg - The message body

-- pFrom - The address of the message sender

-- pCc - Any cc addresses

-- pBcc - Any Bcc addresses

-- pHtml - Boolean, if the message is to be sent as html

-- pAtts - Array of all attachments to send, each attachment of the form:

-- * name:  the name of the attachment

-- * path:  the absolute path to the attachment

-- * type:  the mime type of the attachment, defaults to

-- application/octet-stream

--

command mail pTo, pSub, pMsg, pFrom, pCc, pBcc, pHtml, pAtts

First of all, we look at the actual calling of sendmail.  As mentioned above, we will call it via the shell function.  Instead of the user typing the message in after calling sendmail, we will pipe our message directly to sendmail.

get shell("echo" && wrapQ(shellEscape(pMsg)) && "| /usr/sbin/sendmail"  && wrapQ(shellEscape(pTo)) && "-f" && wrapQ(shellEscape(pFrom)))

Note the use of helper functions "wrapQ" and "shellEscape".  These functions format strings for use in the shell function.  "wrapQ" just pre and post fixes the passed string with quotation marks while "shellEscape" escapes any special characters.

-- escape shell characters: use this function before passing data to the shell

function shellEscape pText

repeat for each char tChar in "\`!$" & quote

replace tChar with "\" & tChar in pText

end repeat

return pText

end shellEscape

-- wrap quotes around text

function wrapQ pText

return quote & pText & quote

end wrapQ

 

 

Formatting Email Headers

So, our command now sends bare bones emails.  In order for subject, from, to and cc parameters to work correctly, we must add the appropriate headers to our message.  To do this, we just need to prefix our message with the following header data:

From: from@address.com

To: to@address.com

Cc: cc@address.com

Subject: Message Subject

We will do this by creating a new variable for placing the header and message data into:

local tMsg

put "From:" && pFrom & return & "To:" && pTo & return & "Subject:" && pSub & return into tMsg

if pCc is not empty then

put "Cc:" && pCc & return after tMsg

end if

put pMsg & return after tMsg

Note that the BCC addresses are not handled here.  Instead, we just send a copy of the message to the BCC addresses:

if pBcc is not empty then

get shell("echo" && wrapQ(shellEscape(tMsg)) && "| /usr/sbin/sendmail"  && \

wrapQ(shellEscape(pBcc)) && "-f" && wrapQ(shellEscape(pFrom)))

end if

The next parameter in our command's prototype is "pHtml".  This will be a boolean value that will determine if the email is to be sent as plain text or html.  To do this, we just add to our header (before we add the message body) the content type:

if pHtml is true then

put "Content-Type: text/html;" & return & return after tMsg

else

put "Content-Type: text/plain;" & return & return after tMsg

end if

 

Adding Attachments

Our final parameter handles any attachments.  We intend attachments to be passed in the form of an array, with an element for each attachment.  Each attachment will be an array itself, with three elements, the attachments name, path and mime type.  An attachment array will take the following form:

put "/home/text_file.txt" into tAtts[1]["path"]

put "text_file.txt" into tAtts[1]["name"]

put "text/plain" into tAtts[1]["type"]

put "/home/pdf_file.pdf" into tAtts[2]["path"]

put "pdf_file.txt" into tAtts[2]["name"]

put "application/pdf" into tAtts[2]["type"]

For more information about mime type visit http://www.w3schools.com/tags/att_script_type.asp.  In order to send these attachments, we must separate them out from the body of our email.  We do this by define g the email as being multipart.  That entails that the email body will have multiple sections, one being our message body, the remainder our attachments.  Each section will be split by a unique boundary string.  We do this by stating in our email will be multipart and defining our boundary string in the header:

if pAtts is an array then

local tBoundary

put "boundary" & the seconds into tBoundary

put "MIME-Version: 1.0" & return & "Content-Type: multipart/mixed; boundary=" & \

wrapQ(tBoundary) & return & "--" & tBoundary & return after tMsg

end if

Next we need to add our attachments to the email, after the message body.  We do this by adding the base 64 encoded contents of the attachment on to the end of our message, remembering to separate each with our boundary string:

if pAtts is an array then

put "--" & tBoundary & return after tMsg

repeat for each element tAtt in pAtts

put "Content-Type:" && tAtt["type"]  & "; name=" & wrapQ(tAtt["name"]) & ";" & \

return & "Content-Transfer-Encoding: base64;" & return & return & \

base64Encode(URL ("binfile:" & tAtt["path"])) & return & "--" & tBoundary & \

return after tMsg

end repeat

end if

 

The Complete Command

So that's it, our command is now complete.  With all our sections added in, it should look something like the following:

-- mail

--

-- Emails the given message to the recipients specified.

-- Each address passed can have a name  attached in the form "name <address>".

-- Addresses can be passed as comma separated lists.

-- Attachements can be added by passing an array (interger indexed or otherwise).

-- with each attachment itself being an array.

--

-- pTo - The addresses to send the message to

-- pSub - The message subject

-- pMsg - The message body

-- pFrom - The address of the message sender

-- pCc - Any cc addresses

-- pBcc - Any Bcc addresses

-- pHtml - Boolean, if the message is to be sent as html

-- pAtts - Array of all attachments to send, each attachment of the form:

-- * name:  the name of the attachment

-- * path:  the absolute path to the attachment

-- * type:  the mime type of the attachment, defaults to

-- application/octet-stream

--

command mail pTo, pSub, pMsg, pFrom, pCc, pBcc, pHtml, pAtts

local tMsg

-- build the message header, adding the from, to and subject details

-- we also put any cc addresses in here, but not bcc (bcc addresses hidden)

put "From:" && pFrom & return & "To:" && pTo & return & "Subject:" && pSub & \

return into tMsg if pCc is not empty then

put "Cc:" && pCc & return after tMsg

end if

-- if there are any attachments, we must send this email as multipart

-- with the message body and each attachment forming a part

-- we do this by specifying the message as multipart and generating a unique boundary

if pAtts is an array then

local tBoundary

put "boundary" & the seconds into tBoundary

put "MIME-Version: 1.0" & return & "Content-Type: multipart/mixed; boundary=" & \

wrapQ(tBoundary) & return & "--" & tBoundary & return after tMsg

end if

-- add the actual message body, setting the content type appropriatly

if pHtml is true then

put "Content-Type: text/html;" & return & return after tMsg

else

put "Content-Type: text/plain;" & return & return after tMsg

end if

put pMsg & return after tMsg

-- add each attachment as a new part of the message, sepearting using

-- the generated boundary

if pAtts is an array then

put "--" & tBoundary & return after tMsg

repeat for each element tAtt in pAtts

if there is a file tAtt["path"] then

if tAtt["type"] is empty then

get "application/octet-stream"

else

get tAtt["type"]

end if

put "Content-Type:" && it & "; name=" & wrapQ(tAtt["name"]) & ";" & \

return & "Content-Transfer-Encoding: base64;" & return & return & \

base64Encode(URL ("binfile:" & tAtt["path"])) & return & "--" & \

tBoundary & return after tMsg

end if

end repeat

end if

-- send the mail by piping the message we have just built to the sendmail command

-- we must also send a copy of the message to the bcc addresses

get shell("echo" && wrapQ(shellEscape(tMsg)) && "| /usr/sbin/sendmail"  && \

wrapQ(shellEscape(pTo)) && "-f" && wrapQ(shellEscape(pFrom)))

if pBcc is not empty then

get shell("echo" && wrapQ(shellEscape(tMsg)) && "| /usr/sbin/sendmail"  && \

wrapQ(shellEscape(pBcc)) && "-f" && wrapQ(shellEscape(pFrom)))

end if

end mail

3 Comments

Shedo Surashu

nice ^^

Tim Selander

Hi, I'm a newbie at rev and know zero about Unix, shell and sendmail commands. Having a bit of a hard time following this. I don't need attachments, but do need to add these headers:

Content-Type: text/plain; charset=iso-2022-jp
Content-Transfer-Encoding: 7bit

I have a variation of this script working (with help of Sarah Reichelt) but can you show me what the script would look like to add these two headers? Thanks.

Mark Wieder

Change the section of code that sets the headers to read:

if pHtml is true then
put "Content-Type: text/html; charset=iso-2022-jp " & return after tMsg
else
put "Content-Type: text/plain; charset=iso-2022-jp " & return after tMsg
end if
put "Content-Transfer-Encoding: 7bit" & return & return after tMsg

Add your comment

E-Mail me when someone replies to this comment