# The SMTP driver

The SMTP driver sends your app's email through a mail server you name. A
connection with `driver: Driver.smtp` gives:

- `host:`, the server's name, written out or read from a setting built
  on `Text`;
- `port:`, which is 587 when you leave it out: the submission port,
  which Cloudflare Workers allows;
- `credential: { username: password: }`, each read from a setting, the
  password from a secret one.

A mistake in any of them is `connection.key_invalid`,
`connection.key_missing` or `connection.value_invalid`
([Connecting in `app.jadpo`](connecting-in-app-jadpo.md)).

```jadpo
application {
    connections: {
        mail: {
            driver: Driver.smtp
            host: config.smtp_host
            port: 587
            credential: {
                username: config.smtp_username
                password: config.smtp_password
            }
        }
    }
    email: {
        connection: mail
        from: config.mail_sender
    }
}
```

## TLS

The driver always uses TLS, except to a server on the same machine. It
upgrades the connection with `STARTTLS`, or speaks TLS from the start on
port 465, and checks the server's certificate and name. Anywhere else, a
server that offers no TLS, or whose certificate fails the check, makes
the send `misconfigured`, so a credential never goes in the clear.

A server on the same machine (`localhost`, `127.0.0.1`, `::1`) is spoken
to without `STARTTLS`, whether it offers it or not, as a database there
is ([PostgreSQL over TLS](stores.md#postgresql-over-tls)). A stock
Postfix offers `STARTTLS` with a self-signed certificate that no check
passes.

## The message it writes

Lines end in CRLF and every part is in base64. The text and the HTML go
as `multipart/alternative`, with attachments beside them in
`multipart/mixed`. A subject that isn't plain ASCII is written as RFC
2047 encoded words, and `Date:` comes from the host's clock. The server
adds the `Message-ID:`, as RFC 6409 says a submission server does.

## When a send goes wrong

The driver classifies what happened as every driver does
([The seam every outside call goes through](the-seam-every-outside-call-goes-through.md)):

- A server that can't be reached, refuses for now (`4xx`), or drops the
  connection before the end of the message went out surely took nothing,
  so the delivery is retried.
- Once the end of the message went out, no answer may still mean the
  message was taken. SMTP takes no idempotency key, so the delivery is
  parked as `outcome_unknown`.
- A recipient refused for good (`5xx`) fails as `recipient_rejected`, and
  a refused message as `message_rejected`.
- A refused credential or sender is `misconfigured`.
