smtp is a Simple Mail Transfer Protocol client in Tcllib.

See Also

SMTP with attachments
SMTP Mail with Jim


official reference


smtp::sendmessage (at least up to 1.4.5) will silently fail to authenticate if [package require SASL] fails. If you're using teacup, make sure SASL is available, as it wasn't listed as a dependency for SMTP as of 2011-01-19.

Here's a minimal useful example:

proc send_simple_message {recipient email_server subject body} {
    package require smtp
    package require mime

    set token [mime::initialize -canonical text/plain -string $body]
    mime::setheader $token Subject $subject
    smtp::sendmessage $token \
            -recipients $recipient -servers $email_server
    mime::finalize $token

send_simple_message [email protected] localhost \
                "This is the subject." "This is the message."

OK, this is more minimal:

set token [mime::initialize -canonical text/plain -string $body]
smtp::sendmessage $token \
        -header [list Subject $subject] \
        -header [list To $recipient]
mime::finalize $token

WHD: This last one didn't work for me; I kept getting an error from sendmessage about { } not being a valid option.

See also

PT 2004-07-08: Here is a rather more complex example that can handle an authenticating SMTP server.

set tok [mime::initialize -canonical text/plain -string $TEXT]
smtp::sendmessage $tok \
    -servers [list $SERVER] -ports [list $PORT] \
    -usetls 1 \
    -username $USERNAME \
    -password $PASSWORD \
    -header [list From "$FROM"] \
    -header [list To "$TO"] \
    -header [list Subject "$SUBJECT"] \
    -header [list Date "[clock format [clock seconds]]"]    
mime::finalize $tok

Note: if you have trouble with sending mail, adding -debug 1 as an option to the [sendmessage] command will log the SMTP conversation and may help diagnose problems.

MHo 2007-Sep-20:

  • Can't find -debug 1 on the man page.
  • Where does the output goes to? I see nothing.

PT: Not being on the man page doesn't mean it's not there. Output is to stderr. MHo: Thanks. Just couldn't catch this. Now using console show and I see what I wanted.-

CMcC 2005-02-20: I have put together a first cut asynchronous SMTP client here [L1 ] ... I'd be interested to see if it works for people.

The design is moderately interesting. It's a snit object which is mostly composed of methods of the form X-Y where X is the protocol command last issued, and Y is a glob to match the server's response code. This is a finite state machine, and the fileevent bubbles through it driven by responses from the server.

Given this FSA design, it should be easy enough to use the tcllib finite state machine to drive it, too, if that were desired. I have reservations about too great a reliance on FSA as a programming technique, though.

escargo 2007-09-20: Which finite state machine in tcllib do you mean? The mentions I saw all related to the grammar package.

JMN 2006-03-30:

I think the strong Tie-in (as it currently stands) between the MIME package and client SMTP library here is a little unfortunate. This is not ideal for a whole class of applications where the message is already in mime format but needs to be submitted to an SMTP server - I'm putting this note here lest people try building mail components using this library - and in unnecessarily re-parsing mime messages find the performance (especially with large messages) woeful..

Even using the '-canonical text/plain' option during mime::initialize doesn't help much because a) If you already have a message that includes all the headers - this header information ends up in the *body* of the new message. - This means you need to separate out your headers from the body prior to calling sendmessage. Presumably this means for a multipart message you'd have to use the MIME package rather than simply splitting on the first blank line. i.e unnecessary re-parsing of the whole (potentially very large) message.

b) This smtp library forces you to load the entire message in memory - i.e it doesn't support streaming large messages from a Tcl channel to the SMTP socket. (not really a complaint about the mime/smtp tie-in - but something to take note of.)

It should be noted that Tcl can be used very effectively for SMTP & message handling applications. I've built an SMTP Message Submission Agent entirely in TCL that receives multiple multi-megabyte mime-formatted messages concurrently with no trouble on very modest hardware. The messages arrive already mime-encoded.. and after an intermediate stage on disk, are submitted to another SMTP server with a few additional header lines. The only use of the tcllib MIME package in this case was for the handy mime::parseaddress feature.

In 2008-05 Twylite wrote comp.lang.tcl a message showing an example of code in response to a question of why a user's smtp attempts were returning an error saying that the message-id header was missing:

We've seen that when using SMTP AUTH in conjunction with MS Exchange. The problem was related to the use of mime::setheader instead of including the headers as options to smtp::sendmessage. Not sure _why_ this was a problem, it just was.

This code works for us:

package require mime 
package require smtp 

proc send_email {from to subject body} { 
    set opts {} 
    lappend opts -servers [list] 
    lappend opts -ports [list 25] 
    lappend opts -username user 
    lappend opts -password pass 
    lappend opts -header [list Subject $subject] 
    lappend opts -header [list From $from] 
    lappend opts -header [list To $to] 
    set mime_msg [mime::initialize -canonical text/plain -encoding 7bit -string $body] 
    smtp::sendmessage $mime_msg {*}$opts -queue false -atleastone false -usetls false 
    mime::finalize $mime_msg 

send_email {Twylite <twylite@mydomain>} {Person <someone@somewhere>} Test { 
  This is my mail message. 

PatJansen 2012-09-13 11:26:53:

I spent several days trying to send email through both and my local MS Exchange using authentication but without success. I finally managed to get it working using the code below. This procedure was run from within PostgreSQL using pltclu.

I hope it helps at least one person.

create or replace function pgmail(text, text, text, text,text,text,text,text) returns int4 AS $$
package require smtp
package require mime
set mailfrom $1
set mailto $2
set mailsubject $3
set body $4
set myHost $5
set myPort $6
set myUname $7
set myUpassword $8

set token [mime::initialize -canonical text/plain -encoding 7bit -string $body]
    mime::setheader $token Subject $mailsubject
    smtp::sendmessage $token \
        -servers [list $myHost] -ports [list $myPort]\
        -usetls true\
        -debug true\
        -username $myUname \
        -password $myUpassword\
        -queue false\
        -atleastone true\
        -header [list From $mailfrom] \
        -header [list To $mailto] \
        -header [list Subject $mailsubject]\
        -header [list Date [clock format [clock seconds]]]
    mime::finalize $token
return 1
$$ LANGUAGE pltclu;>

Tls Support

See this note in the official documentation.

See also (Tcl) Sending e-mails via gmail and yahoo mail servers .


Gmailuser 2016-08-08:


This does not work for online email providers like Yahoo or Gmail. In fact, none of the other smtp related code on this wiki works.

Gmail and Yahoo use port 587 and require TLS. Even when these are enabled in the code above, one can't send email messages due to various errors such as socket and channel errors, unexpected end of file, etc.

I would like to use Tcl to send messages to myself at my gmail account and using gmail as the smtp provider. Is this possible at all or does Tcl only work with basic SMTP at port 25?

Bezoar 2020-04-05:

Gmail has tightened its security and requires you to either accept 2-Factor authentication or reduced security. The examples above work with port 587 ( all others being equal) and with the reduced security option. You have to login to your google account go to the security section and specifically turn on 'less secure access'. I had done this before and was surprised I had to turn this back on again so they may periodically may turn it off if you don't use it often. If you use 2-Factor authentication you can generate an app password as detailed in this resource: [L2 ] You simply get an app password to use rather than your own. The limitation is that it will work only on one device ( not sure how google defines the device ). I haven't tested it but it is likely that the same password can be used on multiple hosts behind a NAT'ed router. If anyone tries it out please update here.