How to send e-mail using the tsNet external
Getting started with sending e-mail
The tsNet external that comes bundled with the commercial versions of LiveCode supports the ability to send e-mail through most SMTP servers. It supports connecting to:
- Plain unencrypted SMTP servers
- SMTP servers using SSL encryption
- SMTP servers using TLS encryption
It is important to know which methods your SMTP server supports, what ports it uses and if it requires authentication before going any further.
An example using the tsNetSmtpSync function
The simplest version of the tsNet SMTP functions is tsNetSmtpSync. The syntax for this function is
tsNetSmtpSync(pURL, pFrom, pRcpt, pData, pOutHeaders, pBytes, [pSettings])
This function will connect to the specified mail server and send a single message to a number of recipients. If an error occurs while sending the e-mail, the function will return a string that begins with "tsneterr:".mail delivery. This is particularly common for mailing lists and "bcc" recipient addresses.
Lay out the Stack
NOTE: You must ensure to select “MIME Library” as well as “Internet” and “tsNet” from the Inclusions tab of “Standalone Application Settings” before you can run this as a standalone.
Create a new stack then drag a button and five fields onto it.
Set the name of the fields to "email_from", "email_to", "email_cc", "email_subject" and "email_message".
Drag out a label beside each field and set a name for them. For example, "Sender address" for the first label.
Rename the button, "Add attachments and send".
Save this stack.
Create the Script
Edit the script of the button that you placed on the stack by right clicking on the button and selecting "edit script".
Add the following code.
on mouseUp pMouseButton
local tUrl, tEmailMessage, tRecipient, tBody, tFrom, tTo, tCc, tSubject, tAttachments
local tSettings, tResult, tBytes, tResponseHeaders
-- Specify the e-mail server settings
put "smtp://my.mailserver.com:587/" into tUrl
put "username" into tSettings["username"]
put "password" into tSettings["password"]
-- Enable TLS for SMTP
put true into tSettings["use_ssl"]
-- Encode the e-mail message body
put mimeEncodeFieldAsMIMEMultipartDocument(the long id of field "email_message") into tBody
-- See if the user wants to add an attachment
if the environment is "mobile" then
answer "Do you wish to include an image from your photo library?" with "Yes" or "No"
if it is "Yes" then
set the lockloc of the templateimage to true
set the width of the templateimage to "300"
set the height of the templateimage to "400"
mobilePickPhoto "library"
put the text of the last image into tAttachments[1]["data"]
put "image/png" into tAttachments[1]["mimetype"]
put "image.png" into tAttachments[1]["name"]
answer "Attaching photo from library"
delete the last image
else
answer "No image attached"
end if
else
answer file "Select a file to attach:"
if the result is not "Cancel" then
put it into tAttachments[1]["filepath"]
answer "Attaching file:" && tAttachments[1]["filepath"]
else
answer "No file attached"
end if
end if
-- Encode the e-mail headers and body
put field "email_from" into tFrom
put field "email_to" into tTo
put field "email_cc" into tCc
put field "email_subject" into tSubject
mimeEncodeAsMIMEEmail tBody, tFrom, tTo, tCc, tSubject, tAttachments
put it into tEmailMessage
-- Make tRecipient a list of all recipients (To and Cc)
put tTo & cr & tCc into tRecipient
-- Send the e-mail
put tsNetSmtpSync(tURL, tFrom, tRecipient, tEmailMessage, tResponseHeaders, tBytes, tSettings) into tResult
-- Check the result
if the first word of tResult is "tsneterr:" then
answer "Error" && tResult && "returned from server"
else
answer "E-mail sent"
end if
end mouseUp
What the Script does
Let us look at the various parts of that script to understand what is going on in more detail.
Firstly, we store the address of the mail server to be used in a variable called tURL.
put "smtp://mymailserver.domain.com:587/" into tUrl
The address needs to be specified in one of the following formats:
- For standard unencrypted SMTP connections - smtp://mailserver.domain.com:25
- For SSL encrypted SMTP connections - smtps://mailserver.domain.com/
- For TLS encrypted SMTP connections - smtp://mailserver.domain.com:587
Note: For any of these formats, a port number may be specified if your mail server does not use the standard SMTP ports.
If your mail server requires authentication, you can store the username and password to be used by the function in an array. In this case, we are using an array called tSettings. The "username" and "password" keys are used to store these values.
put "smtp_username" into tSettings["username"]
put "smtp_password" into tSettings["password"]
The next line of the script is very important.
-- Enable TLS for SMTP
put true into tSettings["use_ssl"]
As well as the username and password required to send e-mails, nother key that can be set is "use_ssl".
When using TLS encryption with a SMTP server, this key must be set to true. If you are using unencrypted SMTP, or SMTP via SSL, this should be left out or set to false.
Next we use functions from LiveCode’s MIME library to encode the body of the e-mail in the MIME format, suitable for sending via SMTP.
-- Encode the e-mail message body
put mimeEncodeFieldAsMIMEMultipartDocument(the long id of field "email_message") into tBody
We also check to see if the user wants to add an attachment to the e-mail and store that ready for encoding.
-- See if the user wants to add an attachment
if the environment is "mobile" then
answer "Do you wish to include an image from your photo library?" with "Yes" or "No"
if it is "Yes" then
set the lockloc of the templateimage to true
set the width of the templateimage to "300"
set the height of the templateimage to "400"
mobilePickPhoto "library"
put the text of the last image into tAttachments[1]["data"]
put "image/png" into tAttachments[1]["mimetype"]
put "image.png" into tAttachments[1]["name"]
answer "Attaching photo from library"
delete the last image
else
answer "No image attached"
end if
else
answer file "Select a file to attach:"
if the result is not "Cancel" then
put it into tAttachments[1]["filepath"]
answer "Attaching file:" && tAttachments[1]["filepath"]
else
answer "No file attached"
end if
end if
If the user selects a file to attach or a photo on a mobile device, the details of these attachments will be stored in a numerically indexed array called tAttachments. This is required so that we can encode the attachments in MIME format before sending the e-mail.
Next we get the sender, recipient and subject details for the e-mail and encode them using the MIME library along with the e-mail body and any attachments.
-- Encode the e-mail headers and body
put field "email_from" into tFrom
put field "email_to" into tTo
put field "email_cc" into tCc
put field "email_subject" into tSubject
mimeEncodeAsMIMEEmail tBody, tFrom, tTo, tCc, tSubject, tAttachments
put it into tEmailMessage
After encoding the e-mail message in MIME format and setting the mail server into variables, there is one other piece of information that the script needs to deal with before the tsNetSmtpSync function can be called.
-- Make tRecipient a list of all recipients (To and Cc)
put tTo & cr & tCc into tRecipient
tsNet requires a complete list of all the recipient e-mail addresses - one per line - regardless of whether they are To, Cc or Bcc stored in one variable. Note that tsNetSmtpSync does not use the values stored within the contents of the e-mail message, as they may not match the actual addresses used for the e-mail delivery. This is particularly common for mailing lists and "bcc" recipient addresses.
Now that we have all that information, we can make the actual call to tsNetSmtpSync.
put tsNetSmtpSync(tURL, tFrom, tRecipient, tEmailMessage, tResponseHeaders, tBytes, tSettings) into tResult
Most of the parameters to the function have been discussed above, however you will notice two other variables tResponseHeaders and tBytes variables in the call to tsNetSmtpSync.
The contents of these two variables are replaced when the function returns and provide more information about what happened during the SMTP transaction.
The tResponseHeaders variable will contain the complete SMTP response to all the commands that were sent to the SMTP server. In general, the last line of this variable should contain a 250 response line indicating that the message has been successfully sent to the server.
The tBytes parameter will contain the number of bytes that were sent to the SMTP server.
While this example does not utilise the information provided in these variables, they must be included in every call to the function.
If the e-mail has been sent successfully, the result from the tsNetSmtpSync call will contain the final status code response that is returned from the SMTP server. This will usually be "250".
However, if the e-mail has failed to be sent, the result will start with "tsneterr:" followed by an readable error message and the status code that was returned.
As the final piece of code in this script shows, this makes it very easy to check if the e-mail has been sent.
if the first word of tResult is "tsneterr:" then
answer "Error" && tResult && "returned from server"
else
answer "E-mail sent"
end if
Sample stacks
The code that was used in the above is available as a Livecode stack from this url: https://tinyurl.com/vw2l3ra
Bastian
Hi everybody! Does somebody has an idea why both example stacks didn't work anymore? I got always the error message button "Send Email": execution error at line 38 (Function: error in function handler) near "tsNetSmtpSync", char 8". What did I do wrong?
Elanor Buchanan
Hi Bastian
What version and edition of LiveCode are you using? tsNet is only available in the Commercial editions. I get the same error as you in LiveCode Community but this is to be expected.
If you don't think this is the issue please let us know.
Kind regards,
Elanor
Mario
Hi,
I tested the tsNet send mail as described in this article. The email was send without error, but after a few moments the sender received an email from gmail Mail delivery system which I report below:
----------------------------------------------------------------
This message was created automatically by mail delivery software.
A message that you sent could not be delivered to one or more of its
recipients. This is a permanent error. The following address(es) failed:
*edited*@gmail.com
host gmail-smtp-in.l.google.com [209.85.145.26]
SMTP error from remote mail server after end of data:
550-5.7.1 [50.28.38.223 11] Our system has detected that this message is
550-5.7.1 not RFC 5322 compliant:
550-5.7.1 'From' header is missing.
550-5.7.1 To reduce the amount of spam sent to Gmail, this message has been
550-5.7.1 blocked. Please visit
550-5.7.1 https://support.google.com/mail/?p=RfcMessageNonCompliant
550 5.7.1 and review RFC 5322 specifications for more information. a4si11230332ioe.137 - gsmtp
Reporting-MTA: dns; tio.on-rev.com
Action: failed
Final-Recipient: rfc822; *edited*@gmail.com
Status: 5.0.0
Remote-MTA: dns; gmail-smtp-in.l.google.com
Diagnostic-Code: smtp; 550-5.7.1 [50.28.38.223 11] Our system has detected that this message is
550-5.7.1 not RFC 5322 compliant:
550-5.7.1 'From' header is missing.
550-5.7.1 To reduce the amount of spam sent to Gmail, this message has been
550-5.7.1 blocked. Please visit
550-5.7.1 https://support.google.com/mail/?p=RfcMessageNonCompliant
550 5.7.1 and review RFC 5322 specifications for more information. a4si11230332ioe.137 - smtp
----------------------------------------------------------
I used my mail.XXXX.on-rev.com account to send the eMail.
Any ideas why the email is not RFC 5322 compliant?
Thanks
Mario
Matthias Rebbe
Hi Mario,
it seems Google is very strict and does not allow any emails which are not full RFC compliant.
The lesson is somehow outdated. There are new functions and commands in LC available or building all needed e-mail headers/message bodies. So instead of building the e-mail manually you could use
mimeEncodeFieldAsMIMEMultipartDocument for building the e-mail body
and
mimeEncodeAsEmail for building the the mime encoded e-mail with all needed e-mail headers.
Let´s say you have the fields From,To, Subject,Body,Server, User and Password in your stack. Then a simple script would look like this:
put mimeEncodeFieldAsMIMEMultipartDocument(the long id of field "body") into tBody
put fld "from" into tSender
put fld "to" into tRecipient
put fld "subject" into tSubject
put fld "user" into tSettings["username"]
put fld "password" into tSettings["password"]
-- if the connection is TLS then uncomment the following line
-- put TRUE into tSettings["use_ssl"]
put fld "server" into tURL
mimeEncodeAsMimeEmail tbody,tSender,tRecipient,"",tSubject
-- the result contains any error
-- it contains the mime encoded e-mail
if the result is not empty
then
answer error "Error creating the complete e-mail"
exit to top
end if
put tsNetSmtpSync(tURL, tSender, tRecipient, it, tResponseHeaders, tBytes, tSettings) into tResult
if the first word of tResult is "tsneterr:" then
answer "Error" && tResult && "returned from server"
else
answer "E-mail sent"
end if
Matthias
Charles Warwick
Hi Mario,
Matthias is correct, this lesson will be updated in the next couple of weeks to make the example email more compliant with standards.
Regards,
Charles.
Mario
Hello again,
any news on the update of this lesson? I would like to send automatically within Livecode an email with a pdf (generated with Livecode) attachment. Any ideas on how to do that with tsNet ?
Thanks,
Mario
Charles Warwick
Hi Mario,
The lesson has now been updated - hope you find it helpful.
Regards,
Charles.
Roberto
Supposing that this works on mobile too, it would be nice to show how to select a file (photo, file, other ?) from the mobile to make an attachment.
Thanks
Roberto
To my dismal, after implementing in my standalone, I discovered that this does not work on mobile"
May I suggest, at the beginning of each lesson, to notify with platform are supported. a date would be nice too, in order to understand how old are the recipes)
Matthias Rebbe
Hi Roberto,
do you get an tsNet error back? On which mobile platform and what os version are you trying? The external is available in Indy and Business versions of LC and supports also iOS and Android, so it should work.
Roberto
Opps! Forgot to enable the MIME extension. By the way, should a " tsNetInit" and a "tsNetClose" be called in the send script, if in the standalone the email sending is the only activity requiring it?
Matthias Rebbe
Good to know that it is working. The MIME extension got me also in the past several times and i am sure i will get me also in the future from time to time. ;) Unfortunately it has to be manually selected, because the automatic inclusion detection does work with that library.
If i remember correctly then tsNetInit is only needed when using the tsNet external with LivecodeServer. On mobile it should work w/o calling tsNetInit first. If you're sure that your app only sends once per App start then you could call tsNetClose to disable it, but i do not see any reason, why this is really needed.
Elanor Buchanan
Hi Roberto
If you want to choose something to include on mobile I suggest taking a look at the mobilePick commands. That might be a good starting point.
Kind regards
Elanor
Charles Warwick
Hi Roberto,
We have updated this lesson to show an example of how to send an attachment on a mobile platform as well as on a desktop.
You do not need to call tsNetInt directly if you have included the "Internet" library.
Regards,
Charles
Franziska
hey I have the same problem as bastian from 2018. I only have the community version, does that mean, the code can not work? is there a way to fix this without getting the commercial editions?
thanks
Elanor Buchanan
Hi Franziska
Yes, tsNet is only available in Indy or Business. If you just want to be able to send an email you could use revMail or mobileComposeMail(depending on the platform). This will open an email in the user's email program.
If you want to send email via a server you might find something useful in this forum discussion.
https://forums.livecode.com/viewtopic.php?f=11&t=34668
Kind regards
Elanor
Matthias Rebbe
@Franziska
As Elanor already wrote tsNet is not available in the Community edition of Livecode.
Among the possibilities Elanor already mentioned there are also others.
1. use an smtp library. Sarah Reichelt created one long time ago. It does not support TLS/SSL. So only unencrypted communication using port 25 is possible. You'll find it here
https://github.com/trozware/rev_stacks
2. For Windows and macOS you could use a 3rd party tool, e.g. CURL for sending the emails. In your Livecode script you have to create the complete email data according RFC , the same way as you would do for sending with tsNET. But instead of calling any tsNET function/command you are using the shell() function in LC to call CURL for sending the email.
I've done so even with the commercial license of LC until tsNet was released.
Here at https://everything.curl.dev/usingcurl/smtp you find information about sending emails with CURL.
Curl is already bundled with macOS, for Windows you have to download a version which you then have to include with your final standalone.
Downloads can be found here
https://curl.se/download.html
Regards,
Matthias