Sending MailΒΆ

Sometimes, your application may want to send an email. Below is a function which you can use to send mail over an encrypted connection and authenticating to the server:

esmtpmail.py

from cStringIO import StringIO
from twisted.internet import reactor, endpoints, defer
from twisted.mail.smtp import ESMTPSenderFactory

def sendMail(username, password, host, port, msg):
    """
    Send an email message using authenticated and encrypted SMTP.

    @param username: Username to use.
    @param password: Password to use.
    @param host: SMTP server's hostname or IP address.
    @param port: The port to connect to for SMTP.
    @param msg: Email to send.
    @type msg: L{email.message.Message}

    @return: A L{Deferred} that fires with the result of the email being
             sent.
    """
    resultDeferred = defer.Deferred()

    fp = StringIO(msg.as_string(unixfrom=True))

    senderFactory = ESMTPSenderFactory(username, password, msg.get("From"),
        msg.get("To"), fp, resultDeferred)

    endpoints.HostnameEndpoint(reactor, host, port).connect(senderFactory)

    return resultDeferred

How can it be encrypted if the reactor connects over TCP, and not SSL/TLS? Not to worry – ESMTP implements STARTTLS (you can find more details on it here). This ‘upgrades’ the connection from cleartext to an encrypted one before any sensitive data is sent.

The implementation of ESMTPSenderFactory, which this code uses, requires the use of encryption (see ESMTPSender‘s source).

Here is an example, using the above:

email_example.py

from __future__ import print_function
from esmtpmail import sendMail # the code above
from email import message
from twisted.internet.task import react

def main(reactor):

    m = message.Message()
    m.add_header("To", "hawkowl@atleastfornow.net")
    m.add_header("From", "hawkowl@atleastfornow.net")
    d = sendMail("hawkowl@atleastfornow.net", "password",
                 "mail.atleastfornow.net", 587, m)
    d.addCallback(print)
    return d

react(main)

Upon running it, you will get something like this:

$ python email_example.py
(1, [('hawkowl@atleastfornow.net', 250, '2.1.5 Ok')])

The printed line is the result of the Deferred that is returned by sendMail, once the mail transaction has been completed.

The result is a 2-tuple containing how many addresses the mail was sent successfully to and the sending results. The sending results is a list of 3-tuples containing the email, the SMTP status code (see section 4.2 of the RFC), and the dot-separated ESMTP status code, for each recepient. In the example, the SMTP code of 250 says that everything is OK, and the ESMTP status “2.1.5 Ok” means that the recepient address was valid.