Version 29 of smtp

Updated 2007-09-20 10:50:37 by hoffi

Simple Mail Transfer Protocol

Documentation can be found at

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.

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

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

WHD, CL will make a point of returning in July 2002 to address this error. What's above certainly worked with ... well, *some* version.

See also

PT 8-July-2004: 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.

SV: See also SMTP with attachments

CMcC 050220: 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.


Category Package, subset Tcllib - Category Networking