Introduction

Lettre is an email library that allows creating and sending messages. It provides:

  • An easy to use email builder
  • Pluggable email transports
  • Unicode support (for emails and transports, including for sender et recipient addresses when compatible)
  • Secure defaults (emails are only sent encrypted by default)

The lettre_email crate allows you to compose messages, and the lettre provide transports to send them.

Lettre requires Rust 1.32 or newer. Add the following to your Cargo.toml:

[dependencies]
lettre = "0.9"
lettre_email = "0.9"

Creating messages

This section explains how to create emails.

Simple example

The email part builds email messages. For now, it does not support attachments. An email is built using an EmailBuilder. The simplest email could be:

extern crate lettre_email;

use lettre_email::Email;

fn main() {
    // Create an email
    let email = Email::builder()
        // Addresses can be specified by the tuple (email, alias)
        .to(("user@example.org", "Firstname Lastname"))
        // ... or by an address only
        .from("user@example.com")
        .subject("Hi, Hello world")
        .alternative("<h2>Hi, Hello world.</h2>", "Hi, Hello world.")
        .build();
    
    assert!(email.is_ok());
}

When the build method is called, the EmailBuilder will add the missing headers (like Message-ID or Date) and check for missing necessary ones (like From or To). It will then generate an Email that can be sent.

The text() method will create a plain text email, while the html() method will create an HTML email. You can use the alternative() method to provide both versions, using plain text as fallback for the HTML version.

Sending Messages

This section explains how to manipulate emails you have created.

This mailer contains several different transports for your emails. To be sendable, the emails have to implement SendableEmail, which is the case for emails created with lettre_email.

The following transports are available:

  • The SmtpTransport uses the SMTP protocol to send the message over the network. It is the preferred way of sending emails.
  • The SendmailTransport uses the sendmail command to send messages. It is an alternative to the SMTP transport.
  • The FileTransport creates a file containing the email content to be sent. It can be used for debugging or if you want to keep all sent emails.
  • The StubTransport is useful for debugging, and only prints the content of the email in the logs.

SMTP Transport

This transport uses the SMTP protocol to send emails over the network (locally or remotely).

It is designed to be:

  • Secured: email are encrypted by default
  • Modern: Unicode support for email content and sender/recipient addresses when compatible
  • Fast: supports tcp connection reuse

This client is designed to send emails to a relay server, and should not be used to send emails directly to the destination.

The relay server can be the local email server, a specific host or a third-party service.

Simple example

This is the most basic example of usage:

extern crate lettre;

use lettre::{SendableEmail, EmailAddress, Transport, Envelope, SmtpClient};

fn main() {
    let email = SendableEmail::new(
        Envelope::new(
            Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
            vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
        ).unwrap(),
        "id".to_string(),
        "Hello world".to_string().into_bytes(),
    );
    
    // Open a local connection on port 25
    let mut mailer =
    SmtpClient::new_unencrypted_localhost().unwrap().transport();
    // Send the email
    let result = mailer.send(email);
    
    assert!(result.is_ok());
}

Complete example

extern crate lettre;

use lettre::smtp::authentication::{Credentials, Mechanism};
use lettre::{SendableEmail, Envelope, EmailAddress, Transport, SmtpClient};
use lettre::smtp::extension::ClientId;
use lettre::smtp::ConnectionReuseParameters;

fn main() {
    let email_1 = SendableEmail::new(
        Envelope::new(
            Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
            vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
        ).unwrap(),
        "id1".to_string(),
        "Hello world".to_string().into_bytes(),
    );
    
    let email_2 = SendableEmail::new(
        Envelope::new(
            Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
            vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
        ).unwrap(),
        "id2".to_string(),
        "Hello world a second time".to_string().into_bytes(),
    );
    
    // Connect to a remote server on a custom port
    let mut mailer = SmtpClient::new_simple("server.tld").unwrap()
        // Set the name sent during EHLO/HELO, default is `localhost`
        .hello_name(ClientId::Domain("my.hostname.tld".to_string()))
        // Add credentials for authentication
        .credentials(Credentials::new("username".to_string(), "password".to_string()))
        // Enable SMTPUTF8 if the server supports it
        .smtp_utf8(true)
        // Configure expected authentication mechanism
        .authentication_mechanism(Mechanism::Plain)
        // Enable connection reuse
        .connection_reuse(ConnectionReuseParameters::ReuseUnlimited).transport();
    
    let result_1 = mailer.send(email_1);
    assert!(result_1.is_ok());
    
    // The second email will use the same connection
    let result_2 = mailer.send(email_2);
    assert!(result_2.is_ok());
    
    // Explicitly close the SMTP transaction as we enabled connection reuse
    mailer.close();
}

You can specify custom TLS settings:

extern crate native_tls;
extern crate lettre;
extern crate lettre_email;

use lettre::{
    ClientSecurity, ClientTlsParameters, EmailAddress, Envelope, 
    SendableEmail, SmtpClient, Transport,
};
use lettre::smtp::authentication::{Credentials, Mechanism};
use lettre::smtp::ConnectionReuseParameters;
use native_tls::{Protocol, TlsConnector};

fn main() {
    let email = SendableEmail::new(
        Envelope::new(
            Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
            vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
        ).unwrap(),
        "message_id".to_string(),
        "Hello world".to_string().into_bytes(),
    );

    let mut tls_builder = TlsConnector::builder();
    tls_builder.min_protocol_version(Some(Protocol::Tlsv10));
    let tls_parameters =
        ClientTlsParameters::new(
            "smtp.example.com".to_string(),
            tls_builder.build().unwrap()
        );

    let mut mailer = SmtpClient::new(
        ("smtp.example.com", 465), ClientSecurity::Wrapper(tls_parameters)
    ).unwrap()
        .authentication_mechanism(Mechanism::Login)
        .credentials(Credentials::new(
            "example_username".to_string(), "example_password".to_string()
        ))
        .connection_reuse(ConnectionReuseParameters::ReuseUnlimited)
        .transport();

    let result = mailer.send(email);

    assert!(result.is_ok());

    mailer.close();
}

Lower level

You can also send commands, here is a simple email transaction without error handling:

extern crate lettre;

use lettre::EmailAddress;
use lettre::smtp::SMTP_PORT;
use lettre::smtp::client::InnerClient;
use lettre::smtp::client::net::NetworkStream;
use lettre::smtp::extension::ClientId;
use lettre::smtp::commands::*;

fn main() {
    let mut email_client: InnerClient<NetworkStream> = InnerClient::new();
    let _ = email_client.connect(&("localhost", SMTP_PORT), None);
    let _ = email_client.command(EhloCommand::new(ClientId::new("my_hostname".to_string())));
    let _ = email_client.command(
                MailCommand::new(Some(EmailAddress::new("user@example.com".to_string()).unwrap()), vec![])
            );
    let _ = email_client.command(
                RcptCommand::new(EmailAddress::new("user@example.org".to_string()).unwrap(), vec![])
            );
    let _ = email_client.command(DataCommand);
    let _ = email_client.message(Box::new("Test email".as_bytes()));
    let _ = email_client.command(QuitCommand);
}

Sendmail Transport

The sendmail transport sends the email using the local sendmail command.

extern crate lettre;

use lettre::sendmail::SendmailTransport;
use lettre::{SendableEmail, Envelope, EmailAddress, Transport};

fn main() {
    let email = SendableEmail::new(
        Envelope::new(
            Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
            vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
        ).unwrap(),
        "id".to_string(),
        "Hello world".to_string().into_bytes(),
    );
    
    let mut sender = SendmailTransport::new();
    let result = sender.send(email);
    assert!(result.is_ok());
}

File Transport

The file transport writes the emails to the given directory. The name of the file will be message_id.txt. It can be useful for testing purposes, or if you want to keep track of sent messages.

extern crate lettre;

use std::env::temp_dir;

use lettre::file::FileTransport;
use lettre::{Transport, Envelope, EmailAddress, SendableEmail};

fn main() {
    // Write to the local temp directory
    let mut sender = FileTransport::new(temp_dir());
    let email = SendableEmail::new(
        Envelope::new(
            Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
            vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
        ).unwrap(),
        "id".to_string(),
        "Hello world".to_string().into_bytes(),
    );
    
    let result = sender.send(email);
    assert!(result.is_ok());
}

Example result in /tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.txt:

b7c211bc-9811-45ce-8cd9-68eab575d695: from=<user@localhost> to=<root@localhost>
To: <root@localhost>
From: <user@localhost>
Subject: Hello
Date: Sat, 31 Oct 2015 13:42:19 +0100
Message-ID: <b7c211bc-9811-45ce-8cd9-68eab575d695.lettre@localhost>

Hello World!

Stub Transport

The stub transport only logs message envelope and drops the content. It can be useful for testing purposes.

extern crate lettre;

use lettre::stub::StubTransport;
use lettre::{SendableEmail, Envelope, EmailAddress, Transport};

fn main() {
    let email = SendableEmail::new(
        Envelope::new(
            Some(EmailAddress::new("user@localhost".to_string()).unwrap()),
            vec![EmailAddress::new("root@localhost".to_string()).unwrap()],
        ).unwrap(),
        "id".to_string(),
        "Hello world".to_string().into_bytes(),
    );
    
    let mut sender = StubTransport::new_positive();
    let result = sender.send(email);
    assert!(result.is_ok());
}

Will log (when using a logger like env_logger):

b7c211bc-9811-45ce-8cd9-68eab575d695: from=<user@localhost> to=<root@localhost>