package letters
Install
Dune Dependency
Authors
Maintainers
Sources
sha256=5cbda38f8c891ae84b55aa27f07d598ea6e0251e4e4bd1435b3fde904efc935a
sha512=06a8612473331bcbcaa6e18743d53e9c3a94f82778ce2883327959c4d4be7b785a975fdad93ae34fd2a15cb1f66635346b10dc54a68cef5fbb0fdd750ff1b9e5
Description
Simple to use SMTP client implementation for OCaml
Published: 27 Aug 2020
README
✉ Letters ·
Letters is a library for creating and sending emails over SMTP using Lwt.
Table of Contents
Use
Purpose of the library is to make it easier to send emails when building systems using OCaml. Currently the API consists of three parts:
configuration
building email messages
sending email messages
Whole API is in lib/letters.mli
that contains also some additional documentation.
Keep in mind that this library is in its early days and the API is changing with every release. Also this is tested only on Linux based systems and testing is pretty weak and manual. Though the library has been used successfully.
Configuration
Most simple use case would look something like:
let conf = Config.make ~username:"myuser" ~password:"mypasswd" ~hostname:"smtp.ethereal.email" ~with_starttls:true
This will use port 587
, uses STARTTLS for encryption and tries automatically find CA certificates for verifying server connection.
Port 587
is default when using STARTTLS. If you set ~with_starttls:false
, then the default port will be 465
.
This library does not support SMTP connections without TLS encryption or authentication. For TLS encryption, this library uses ocaml-tls.
If you want to change the server port you can do it with Config.set_port
(passing None
causes default port to be used):
let conf = Config.make ~username:"myuser" ~password:"mypasswd" ~hostname:"smtp.ethereal.email" ~with_starttls:true
|> Config.set_port (Some 2525)
If the CA certificate auto-detection does not work for you (it's very naïve implementation), you can define path to a certificate bundle or to a single PEM encoded certificate, or you can define path to a folder containing multiple PEM encoded certificate files.
To use a CA certificate bundle (each included certificate needs to be PEM encoded):
let conf = Config.make ~username:"myuser" ~password:"mypasswd" ~hostname:"smtp.ethereal.email" ~with_starttls:true
|> Config.set_ca_cert "/etc/ssl/certs/ca-certificates.crt"
To use a single PEM encoded CA certificate:
let conf = Config.make ~username:"myuser" ~password:"mypasswd" ~hostname:"smtp.ethereal.email" ~with_starttls:true
|> Config.set_ca_cert "/etc/ssl/certs/DST_Root_CA_X3.pem"
To use all PEM encoded certificate files from a folder:
let conf = Config.make ~username:"myuser" ~password:"mypasswd" ~hostname:"smtp.ethereal.email" ~with_starttls:true
|> Config.set_ca_path "/etc/ssl/certs/"
Building emails
Building an email is separated into its own step so that you can use mrmime to generate more complex emails when this simplified API does not work for you.
To use our provided API, you can build three kinds of emails:
Plain
, plain textHtml
, HTML onlyMixed
, multipart/alternative containing both: plain text and HTMl segments
If you're not sure, either use Plain
or Mixed
.
Example of building a plain text email:
let sender = "harry@example.com" in
let recipients =
[
To "larry@example.com";
Cc "bill@example.com";
Bcc "dave@example.com";
]
in
let subject = "HTML only test email" in
let body =
Plain
{|
Hi there,
This is a test email from https://github.com/oxidizing/letters
Regards,
The Letters team
|}
in
let mail = build_email ~from:sender ~recipients ~subject ~body in
Example of building an HTML only email:
let sender = "harry@example.com" in
let recipients =
[
To "larry@example.com";
Cc "bill@example.com";
Bcc "dave@example.com";
]
in
let subject = "HTML only test email" in
let body =
Html
{|
<p>Hi there,</p>
<p>
This is a test email from
<a href="https://github.com/oxidizing/letters">letters</a>
<p>
Regards,<br>
The Letters team
</p>
|}
in
let mail = build_email ~from:sender ~recipients ~subject ~body in
Example of building an email with plain text and HTMl segments:
let sender = "harry@example.com" in
let recipients =
[
To "larry@example.com";
Cc "bill@example.com";
Bcc "dave@example.com";
]
in
let subject = "HTML only test email" in
let text =
{|
Hi there,
This is a test email from https://github.com/oxidizing/letters
Regards,
The Letters team
|}
in
let html =
{|
<p>Hi there,</p>
<p>
This is a test email from
<a href="https://github.com/oxidizing/letters">letters</a>
<p>
Regards,<br>
The Letters team
|}
in
let mail = build_email ~from:sender ~recipients ~subject ~body:(Mixed (text, html, None)) in
Letters.build_email
returns result
so you need to map it accordingly:
let mail = build_email ~from:sender ~recipients ~subject ~body:(Mixed (text, html, None)) in
match mail with
| Ok message -> do_something message
| Error reason -> handle_error reason
Sending emails
Sending is single API call Letters.send
that looks like following (when using config
, sender
, recipients
and message
from previous examples):
send ~config ~sender ~recipients ~message
Return type is Lwt.t
so you need to run it with appropriate Lwt
routines.
Examples
See service-test/test.ml
for complete examples that are using ethereal.email service to test sending emails.
Development
Setup
opam switch create . ocaml-base-compiler.4.08.1
eval $(opam env)
opam install --deps-only -y . --with-test
Build
dune build
Tests
Unit tests
Run with default test
target of dune
:
dune build @runtest
These tests are still somewhat far from good and you need to validate all results manually by checking the test output logs.
Service tests
These tests are somewhat slow and fragile and because of that these are expected to be run manually.
First create ethereal.email account and store account details
curl -d '{ "requestor": "letters", "version": "dev" }' "https://api.nodemailer.com/user" -X POST -H "Content-Type: application/json" > ethereal_account.json
Currently using ethereal.email
service requires non-released version of colombe
and you need to check out the project, commit edf757c58fce58c170c63e8a92d3bc81fe4d32ff
contains the needed fix. Then the version with the fix needs to be pinned in the build env:
# Move to folder where colombe is checked out
pushd /path/to/colombe
# Switch to correct git commit in colombe repo
git switch --detach edf757c58fce58c170c63e8a92d3bc81fe4d32ff
# Switch to use same opam env that is used by letters
eval "$(opam env --switch $(dirs | cut -d ' ' -f 2) --set-switch)"
# Pin this specific version of colombe (and all related packages)
opam pin .
# Finally, return back to letters project
popd
Then execute these tests (actually this runs all tests):
dune build @runtest-all
And finally review that the email is correctly generated in the service:
login to https://ethereal.email/login using credentials from the
ethereal_account.json
check the content of messages: https://ethereal.email/messages
Credits
This project is build on colombe and mrmime libraries and use facteur as starting point.
License
Copyright (c) 2020 Miko Nieminen
Distributed under the MIT License.
Dependencies (11)
Dev Dependencies (5)
-
odoc
with-doc
-
ocamlformat
dev
-
yojson
>= "1.7.0" & with-test
-
alcotest-lwt
>= "1.1.0" & with-test
-
alcotest
>= "1.1.0" & with-test
Used by (2)
-
sihl
>= "0.1.0" & < "0.1.5"
-
sihl-email
< "0.2.0"
Conflicts
None