Packages

A Gleam library for sending emails via SMTP, inspired by the Rust mail-send crate

Current section

Files

Jump to
lumenmail src lumenmail@smtp.erl
Raw

src/lumenmail@smtp.erl

-module(lumenmail@smtp).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/lumenmail/smtp.gleam").
-export([builder/2, tls_mode/2, implicit_tls/2, credentials/2, auth/3, timeout/2, helo_host/2, allow_invalid_certs/2, connect/1, send/2, send_raw/4, close/1, reset/1, noop/1, capabilities/1]).
-export_type([smtp_client_builder/0, smtp_client/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" SMTP client implementation.\n"
" Provides functionality for connecting to SMTP servers and sending emails.\n"
).
-type smtp_client_builder() :: {smtp_client_builder,
binary(),
integer(),
lumenmail@types:tls_mode(),
gleam@option:option(lumenmail@types:credentials()),
integer(),
binary(),
boolean()}.
-type smtp_client() :: {smtp_client,
lumenmail@network:socket(),
lumenmail@types:server_capabilities(),
boolean()}.
-file("src/lumenmail/smtp.gleam", 45).
?DOC(" Creates a new SMTP client builder.\n").
-spec builder(binary(), integer()) -> smtp_client_builder().
builder(Host, Port) ->
Tls_mode = case Port of
465 ->
implicit_tls;
_ ->
start_tls
end,
{smtp_client_builder,
Host,
Port,
Tls_mode,
none,
30000,
<<"localhost"/utf8>>,
false}.
-file("src/lumenmail/smtp.gleam", 63).
?DOC(" Sets the TLS mode.\n").
-spec tls_mode(smtp_client_builder(), lumenmail@types:tls_mode()) -> smtp_client_builder().
tls_mode(Builder, Mode) ->
{smtp_client_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
Mode,
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder)}.
-file("src/lumenmail/smtp.gleam", 71).
?DOC(" Sets implicit TLS mode (port 465).\n").
-spec implicit_tls(smtp_client_builder(), boolean()) -> smtp_client_builder().
implicit_tls(Builder, Use_implicit) ->
case Use_implicit of
true ->
{smtp_client_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
implicit_tls,
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder)};
false ->
{smtp_client_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
start_tls,
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder)}
end.
-file("src/lumenmail/smtp.gleam", 82).
?DOC(" Sets authentication credentials.\n").
-spec credentials(smtp_client_builder(), lumenmail@types:credentials()) -> smtp_client_builder().
credentials(Builder, Creds) ->
{smtp_client_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
{some, Creds},
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder)}.
-file("src/lumenmail/smtp.gleam", 90).
?DOC(" Sets username/password credentials.\n").
-spec auth(smtp_client_builder(), binary(), binary()) -> smtp_client_builder().
auth(Builder, Username, Password) ->
{smtp_client_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
{some, {plain, Username, Password}},
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder)}.
-file("src/lumenmail/smtp.gleam", 102).
?DOC(" Sets connection timeout in milliseconds.\n").
-spec timeout(smtp_client_builder(), integer()) -> smtp_client_builder().
timeout(Builder, Timeout_ms) ->
{smtp_client_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
Timeout_ms,
erlang:element(7, Builder),
erlang:element(8, Builder)}.
-file("src/lumenmail/smtp.gleam", 110).
?DOC(" Sets the hostname used in HELO/EHLO.\n").
-spec helo_host(smtp_client_builder(), binary()) -> smtp_client_builder().
helo_host(Builder, Host) ->
{smtp_client_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
Host,
erlang:element(8, Builder)}.
-file("src/lumenmail/smtp.gleam", 118).
?DOC(" Allow invalid/self-signed TLS certificates.\n").
-spec allow_invalid_certs(smtp_client_builder(), boolean()) -> smtp_client_builder().
allow_invalid_certs(Builder, Allow) ->
{smtp_client_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
Allow}.
-file("src/lumenmail/smtp.gleam", 625).
-spec read_response(lumenmail@network:socket(), boolean(), integer()) -> {ok,
lumenmail@types:smtp_response()} |
{error, lumenmail@types:smtp_error()}.
read_response(Socket, Is_tls, Timeout) ->
lumenmail@network:read_response(Socket, Is_tls, Timeout).
-file("src/lumenmail/smtp.gleam", 607).
-spec send_command(lumenmail@network:socket(), boolean(), binary(), integer()) -> {ok,
nil} |
{error, lumenmail@types:smtp_error()}.
send_command(Socket, Is_tls, Command, Timeout) ->
lumenmail@network:send_command(Socket, Is_tls, Command, Timeout).
-file("src/lumenmail/smtp.gleam", 555).
-spec auth_xoauth2(
lumenmail@network:socket(),
boolean(),
lumenmail@types:credentials(),
integer()
) -> {ok, nil} | {error, lumenmail@types:smtp_error()}.
auth_xoauth2(Socket, Is_tls, Creds, Timeout) ->
Encoded = lumenmail@auth:encode_xoauth2(Creds),
gleam@result:'try'(
send_command(
Socket,
Is_tls,
<<"AUTH XOAUTH2 "/utf8, Encoded/binary>>,
Timeout
),
fun(_) ->
gleam@result:'try'(
read_response(Socket, Is_tls, Timeout),
fun(Response) -> case erlang:element(2, Response) =:= 235 of
true ->
{ok, nil};
false ->
{error,
{authentication_failed,
erlang:element(3, Response)}}
end end
)
end
).
-file("src/lumenmail/smtp.gleam", 530).
-spec auth_cram_md5(
lumenmail@network:socket(),
boolean(),
lumenmail@types:credentials(),
integer()
) -> {ok, nil} | {error, lumenmail@types:smtp_error()}.
auth_cram_md5(Socket, Is_tls, Creds, Timeout) ->
gleam@result:'try'(
send_command(Socket, Is_tls, <<"AUTH CRAM-MD5"/utf8>>, Timeout),
fun(_) ->
gleam@result:'try'(
read_response(Socket, Is_tls, Timeout),
fun(Response) ->
gleam@result:'try'(
case erlang:element(2, Response) =:= 334 of
false ->
{error,
{authentication_failed,
erlang:element(3, Response)}};
true ->
{ok, nil}
end,
fun(_) ->
Challenge = erlang:element(3, Response),
Response_str = lumenmail@auth:encode_cram_md5(
Creds,
Challenge
),
gleam@result:'try'(
send_command(
Socket,
Is_tls,
Response_str,
Timeout
),
fun(_) ->
gleam@result:'try'(
read_response(Socket, Is_tls, Timeout),
fun(Response@1) ->
case erlang:element(2, Response@1)
=:= 235 of
true ->
{ok, nil};
false ->
{error,
{authentication_failed,
erlang:element(
3,
Response@1
)}}
end
end
)
end
)
end
)
end
)
end
).
-file("src/lumenmail/smtp.gleam", 497).
-spec auth_login(
lumenmail@network:socket(),
boolean(),
lumenmail@types:credentials(),
integer()
) -> {ok, nil} | {error, lumenmail@types:smtp_error()}.
auth_login(Socket, Is_tls, Creds, Timeout) ->
gleam@result:'try'(
send_command(Socket, Is_tls, <<"AUTH LOGIN"/utf8>>, Timeout),
fun(_) ->
gleam@result:'try'(
read_response(Socket, Is_tls, Timeout),
fun(Response) ->
gleam@result:'try'(
case erlang:element(2, Response) =:= 334 of
false ->
{error,
{authentication_failed,
erlang:element(3, Response)}};
true ->
{ok, nil}
end,
fun(_) ->
Username = lumenmail@auth:encode_login_username(
Creds
),
gleam@result:'try'(
send_command(Socket, Is_tls, Username, Timeout),
fun(_) ->
gleam@result:'try'(
read_response(Socket, Is_tls, Timeout),
fun(Response@1) ->
gleam@result:'try'(
case erlang:element(
2,
Response@1
)
=:= 334 of
false ->
{error,
{authentication_failed,
erlang:element(
3,
Response@1
)}};
true ->
{ok, nil}
end,
fun(_) ->
Password = lumenmail@auth:encode_login_password(
Creds
),
gleam@result:'try'(
send_command(
Socket,
Is_tls,
Password,
Timeout
),
fun(_) ->
gleam@result:'try'(
read_response(
Socket,
Is_tls,
Timeout
),
fun(Response@2) ->
case erlang:element(
2,
Response@2
)
=:= 235 of
true ->
{ok,
nil};
false ->
{error,
{authentication_failed,
erlang:element(
3,
Response@2
)}}
end
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/lumenmail/smtp.gleam", 477).
-spec auth_plain(
lumenmail@network:socket(),
boolean(),
lumenmail@types:credentials(),
integer()
) -> {ok, nil} | {error, lumenmail@types:smtp_error()}.
auth_plain(Socket, Is_tls, Creds, Timeout) ->
Encoded = lumenmail@auth:encode_plain(Creds),
gleam@result:'try'(
send_command(
Socket,
Is_tls,
<<"AUTH PLAIN "/utf8, Encoded/binary>>,
Timeout
),
fun(_) ->
gleam@result:'try'(
read_response(Socket, Is_tls, Timeout),
fun(Response) -> case erlang:element(2, Response) =:= 235 of
true ->
{ok, nil};
false ->
{error,
{authentication_failed,
erlang:element(3, Response)}}
end end
)
end
).
-file("src/lumenmail/smtp.gleam", 456).
-spec authenticate(
lumenmail@network:socket(),
boolean(),
lumenmail@types:credentials(),
lumenmail@types:server_capabilities(),
integer()
) -> {ok, nil} | {error, lumenmail@types:smtp_error()}.
authenticate(Socket, Is_tls, Creds, Caps, Timeout) ->
gleam@result:'try'(
begin
_pipe = lumenmail@auth:select_best_mechanism(
erlang:element(3, Caps),
Creds
),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {authentication_failed, Field@0} end
)
end,
fun(Mechanism) -> case Mechanism of
auth_plain ->
auth_plain(Socket, Is_tls, Creds, Timeout);
auth_login ->
auth_login(Socket, Is_tls, Creds, Timeout);
auth_cram_md5 ->
auth_cram_md5(Socket, Is_tls, Creds, Timeout);
auth_x_o_auth2 ->
auth_xoauth2(Socket, Is_tls, Creds, Timeout)
end end
).
-file("src/lumenmail/smtp.gleam", 401).
-spec parse_capabilities(binary()) -> lumenmail@types:server_capabilities().
parse_capabilities(Ehlo_response) ->
Lines = gleam@string:split(Ehlo_response, <<"\n"/utf8>>),
Caps = lumenmail@types:empty_capabilities(),
_pipe@5 = gleam@list:fold(
Lines,
Caps,
fun(Caps@1, Line) ->
Line@1 = gleam@string:trim(Line),
Upper = string:uppercase(Line@1),
case gleam_stdlib:string_starts_with(Upper, <<"STARTTLS"/utf8>>) of
true ->
{server_capabilities,
true,
erlang:element(3, Caps@1),
erlang:element(4, Caps@1),
erlang:element(5, Caps@1),
erlang:element(6, Caps@1),
erlang:element(7, Caps@1),
erlang:element(8, Caps@1)};
false ->
case gleam_stdlib:string_starts_with(
Upper,
<<"AUTH "/utf8>>
) of
true ->
Mechs = begin
_pipe = gleam@string:drop_start(Upper, 5),
_pipe@1 = gleam@string:split(
_pipe,
<<" "/utf8>>
),
gleam@list:filter_map(
_pipe@1,
fun lumenmail@auth:parse_mechanism/1
)
end,
{server_capabilities,
erlang:element(2, Caps@1),
Mechs,
erlang:element(4, Caps@1),
erlang:element(5, Caps@1),
erlang:element(6, Caps@1),
erlang:element(7, Caps@1),
erlang:element(8, Caps@1)};
false ->
case gleam_stdlib:string_starts_with(
Upper,
<<"8BITMIME"/utf8>>
) of
true ->
{server_capabilities,
erlang:element(2, Caps@1),
erlang:element(3, Caps@1),
true,
erlang:element(5, Caps@1),
erlang:element(6, Caps@1),
erlang:element(7, Caps@1),
erlang:element(8, Caps@1)};
false ->
case gleam_stdlib:string_starts_with(
Upper,
<<"PIPELINING"/utf8>>
) of
true ->
{server_capabilities,
erlang:element(2, Caps@1),
erlang:element(3, Caps@1),
erlang:element(4, Caps@1),
true,
erlang:element(6, Caps@1),
erlang:element(7, Caps@1),
erlang:element(8, Caps@1)};
false ->
case gleam_stdlib:string_starts_with(
Upper,
<<"SIZE"/utf8>>
) of
true ->
Size = begin
_pipe@2 = gleam@string:drop_start(
Upper,
5
),
_pipe@3 = gleam@string:trim(
_pipe@2
),
_pipe@4 = gleam_stdlib:parse_int(
_pipe@3
),
gleam@result:unwrap(
_pipe@4,
0
)
end,
{server_capabilities,
erlang:element(
2,
Caps@1
),
erlang:element(
3,
Caps@1
),
erlang:element(
4,
Caps@1
),
erlang:element(
5,
Caps@1
),
Size,
erlang:element(
7,
Caps@1
),
erlang:element(
8,
Caps@1
)};
false ->
case gleam_stdlib:string_starts_with(
Upper,
<<"DSN"/utf8>>
) of
true ->
{server_capabilities,
erlang:element(
2,
Caps@1
),
erlang:element(
3,
Caps@1
),
erlang:element(
4,
Caps@1
),
erlang:element(
5,
Caps@1
),
erlang:element(
6,
Caps@1
),
true,
erlang:element(
8,
Caps@1
)};
false ->
Caps@1
end
end
end
end
end
end
end
),
(fun(C) ->
{server_capabilities,
erlang:element(2, C),
erlang:element(3, C),
erlang:element(4, C),
erlang:element(5, C),
erlang:element(6, C),
erlang:element(7, C),
begin
_pipe@6 = gleam@list:map(Lines, fun gleam@string:trim/1),
gleam@list:filter(_pipe@6, fun(L) -> L /= <<""/utf8>> end)
end}
end)(_pipe@5).
-file("src/lumenmail/smtp.gleam", 633).
-spec read_multiline_response(lumenmail@network:socket(), boolean(), integer()) -> {ok,
lumenmail@types:smtp_response()} |
{error, lumenmail@types:smtp_error()}.
read_multiline_response(Socket, Is_tls, Timeout) ->
lumenmail@network:read_multiline_response(Socket, Is_tls, Timeout).
-file("src/lumenmail/smtp.gleam", 364).
-spec send_ehlo(lumenmail@network:socket(), boolean(), binary(), integer()) -> {ok,
lumenmail@types:server_capabilities()} |
{error, lumenmail@types:smtp_error()}.
send_ehlo(Socket, Is_tls, Helo_host, Timeout) ->
gleam@result:'try'(
send_command(
Socket,
Is_tls,
<<"EHLO "/utf8, Helo_host/binary>>,
Timeout
),
fun(_) ->
gleam@result:'try'(
read_multiline_response(Socket, Is_tls, Timeout),
fun(Response) ->
case (erlang:element(2, Response) >= 200) andalso (erlang:element(
2,
Response
)
< 300) of
true ->
{ok,
parse_capabilities(erlang:element(3, Response))};
false ->
gleam@result:'try'(
send_command(
Socket,
Is_tls,
<<"HELO "/utf8, Helo_host/binary>>,
Timeout
),
fun(_) ->
gleam@result:'try'(
read_response(Socket, Is_tls, Timeout),
fun(Response@1) ->
case (erlang:element(2, Response@1)
>= 200)
andalso (erlang:element(
2,
Response@1
)
< 300) of
true ->
{ok,
lumenmail@types:empty_capabilities(
)};
false ->
{error,
{command_rejected,
erlang:element(
2,
Response@1
),
<<"HELO failed: "/utf8,
(erlang:element(
3,
Response@1
))/binary>>}}
end
end
)
end
)
end
end
)
end
).
-file("src/lumenmail/smtp.gleam", 598).
-spec ssl_connect(lumenmail@network:socket(), binary(), boolean(), integer()) -> {ok,
lumenmail@network:socket()} |
{error, lumenmail@types:smtp_error()}.
ssl_connect(Socket, Host, Allow_invalid_certs, Timeout) ->
lumenmail@network:ssl_connect(Socket, Host, Allow_invalid_certs, Timeout).
-file("src/lumenmail/smtp.gleam", 575).
-spec check_response_code(lumenmail@types:smtp_response(), integer()) -> {ok,
nil} |
{error, lumenmail@types:smtp_error()}.
check_response_code(Response, Expected_code) ->
case erlang:element(2, Response) =:= Expected_code of
true ->
{ok, nil};
false ->
{error,
{command_rejected,
erlang:element(2, Response),
erlang:element(3, Response)}}
end.
-file("src/lumenmail/smtp.gleam", 594).
-spec tcp_connect(binary(), integer(), integer()) -> {ok,
lumenmail@network:socket()} |
{error, lumenmail@types:smtp_error()}.
tcp_connect(Host, Port, Timeout) ->
lumenmail@network:tcp_connect(Host, Port, Timeout).
-file("src/lumenmail/smtp.gleam", 126).
?DOC(" Connects to the SMTP server.\n").
-spec connect(smtp_client_builder()) -> {ok, smtp_client()} |
{error, lumenmail@types:smtp_error()}.
connect(Builder) ->
gleam@result:'try'(
tcp_connect(
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(6, Builder)
),
fun(Socket) -> gleam@result:'try'(case erlang:element(4, Builder) of
implicit_tls ->
gleam@result:'try'(
ssl_connect(
Socket,
erlang:element(2, Builder),
erlang:element(8, Builder),
erlang:element(6, Builder)
),
fun(Tls_socket) -> {ok, {Tls_socket, true}} end
);
_ ->
{ok, {Socket, false}}
end, fun(_use0) ->
{Socket@1, Is_tls} = _use0,
gleam@result:'try'(
read_response(
Socket@1,
Is_tls,
erlang:element(6, Builder)
),
fun(Response) ->
gleam@result:'try'(
check_response_code(Response, 220),
fun(_) ->
gleam@result:'try'(
send_ehlo(
Socket@1,
Is_tls,
erlang:element(7, Builder),
erlang:element(6, Builder)
),
fun(Capabilities) ->
gleam@result:'try'(
case {erlang:element(4, Builder),
Is_tls,
erlang:element(
2,
Capabilities
)} of
{start_tls, false, true} ->
gleam@result:'try'(
send_command(
Socket@1,
Is_tls,
<<"STARTTLS"/utf8>>,
erlang:element(
6,
Builder
)
),
fun(_) ->
gleam@result:'try'(
read_response(
Socket@1,
Is_tls,
erlang:element(
6,
Builder
)
),
fun(
Response@1
) ->
gleam@result:'try'(
check_response_code(
Response@1,
220
),
fun(
_
) ->
gleam@result:'try'(
ssl_connect(
Socket@1,
erlang:element(
2,
Builder
),
erlang:element(
8,
Builder
),
erlang:element(
6,
Builder
)
),
fun(
Tls_socket@1
) ->
gleam@result:'try'(
send_ehlo(
Tls_socket@1,
true,
erlang:element(
7,
Builder
),
erlang:element(
6,
Builder
)
),
fun(
New_caps
) ->
{ok,
{Tls_socket@1,
true,
New_caps}}
end
)
end
)
end
)
end
)
end
);
{_, _, _} ->
{ok,
{Socket@1,
Is_tls,
Capabilities}}
end,
fun(_use0@1) ->
{Socket@2,
Is_tls@1,
Capabilities@1} = _use0@1,
gleam@result:'try'(
case erlang:element(
5,
Builder
) of
{some, Creds} ->
authenticate(
Socket@2,
Is_tls@1,
Creds,
Capabilities@1,
erlang:element(
6,
Builder
)
);
none ->
{ok, nil}
end,
fun(_) ->
{ok,
{smtp_client,
Socket@2,
Capabilities@1,
Is_tls@1}}
end
)
end
)
end
)
end
)
end
)
end) end
).
-file("src/lumenmail/smtp.gleam", 616).
-spec send_data(lumenmail@network:socket(), boolean(), binary(), integer()) -> {ok,
nil} |
{error, lumenmail@types:smtp_error()}.
send_data(Socket, Is_tls, Data, Timeout) ->
lumenmail@network:send_data(Socket, Is_tls, Data, Timeout).
-file("src/lumenmail/smtp.gleam", 585).
-spec check_response_code_250_251(lumenmail@types:smtp_response()) -> {ok, nil} |
{error, lumenmail@types:smtp_error()}.
check_response_code_250_251(Response) ->
case (erlang:element(2, Response) =:= 250) orelse (erlang:element(
2,
Response
)
=:= 251) of
true ->
{ok, nil};
false ->
{error,
{command_rejected,
erlang:element(2, Response),
erlang:element(3, Response)}}
end.
-file("src/lumenmail/smtp.gleam", 210).
?DOC(" Sends an email message.\n").
-spec send(smtp_client(), lumenmail@message:message()) -> {ok, nil} |
{error, lumenmail@types:smtp_error()}.
send(Client, Msg) ->
gleam@result:'try'(
begin
_pipe = Msg,
_pipe@1 = lumenmail@message:build(_pipe),
gleam@result:map_error(_pipe@1, fun(E) -> {message_rejected, E} end)
end,
fun(Message_str) -> gleam@result:'try'(case erlang:element(2, Msg) of
{some, Addr} ->
{ok, erlang:element(3, Addr)};
none ->
{error,
{message_rejected, <<"No sender address"/utf8>>}}
end, fun(From) ->
Recipients = lumenmail@message:all_recipients(Msg),
gleam@result:'try'(case Recipients of
[] ->
{error,
{message_rejected, <<"No recipients"/utf8>>}};
_ ->
{ok, nil}
end, fun(_) ->
gleam@result:'try'(
send_command(
erlang:element(2, Client),
erlang:element(4, Client),
<<<<"MAIL FROM:<"/utf8, From/binary>>/binary,
">"/utf8>>,
30000
),
fun(_) ->
gleam@result:'try'(
read_response(
erlang:element(2, Client),
erlang:element(4, Client),
30000
),
fun(Response) ->
gleam@result:'try'(
check_response_code(
Response,
250
),
fun(_) ->
gleam@result:'try'(
gleam@list:try_each(
Recipients,
fun(Addr@1) ->
gleam@result:'try'(
send_command(
erlang:element(
2,
Client
),
erlang:element(
4,
Client
),
<<<<"RCPT TO:<"/utf8,
(erlang:element(
3,
Addr@1
))/binary>>/binary,
">"/utf8>>,
30000
),
fun(_) ->
gleam@result:'try'(
read_response(
erlang:element(
2,
Client
),
erlang:element(
4,
Client
),
30000
),
fun(
Response@1
) ->
check_response_code_250_251(
Response@1
)
end
)
end
)
end
),
fun(_) ->
gleam@result:'try'(
send_command(
erlang:element(
2,
Client
),
erlang:element(
4,
Client
),
<<"DATA"/utf8>>,
30000
),
fun(_) ->
gleam@result:'try'(
read_response(
erlang:element(
2,
Client
),
erlang:element(
4,
Client
),
30000
),
fun(
Response@2
) ->
gleam@result:'try'(
check_response_code(
Response@2,
354
),
fun(
_
) ->
gleam@result:'try'(
send_data(
erlang:element(
2,
Client
),
erlang:element(
4,
Client
),
Message_str,
30000
),
fun(
_
) ->
gleam@result:'try'(
send_command(
erlang:element(
2,
Client
),
erlang:element(
4,
Client
),
<<"."/utf8>>,
60000
),
fun(
_
) ->
gleam@result:'try'(
read_response(
erlang:element(
2,
Client
),
erlang:element(
4,
Client
),
60000
),
fun(
Response@3
) ->
gleam@result:'try'(
check_response_code(
Response@3,
250
),
fun(
_
) ->
{ok,
nil}
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end)
end) end
).
-file("src/lumenmail/smtp.gleam", 281).
?DOC(" Sends a raw email with pre-built content.\n").
-spec send_raw(smtp_client(), binary(), list(binary()), binary()) -> {ok, nil} |
{error, lumenmail@types:smtp_error()}.
send_raw(Client, From, To, Data) ->
gleam@result:'try'(
send_command(
erlang:element(2, Client),
erlang:element(4, Client),
<<<<"MAIL FROM:<"/utf8, From/binary>>/binary, ">"/utf8>>,
30000
),
fun(_) ->
gleam@result:'try'(
read_response(
erlang:element(2, Client),
erlang:element(4, Client),
30000
),
fun(Response) ->
gleam@result:'try'(
check_response_code(Response, 250),
fun(_) ->
gleam@result:'try'(
gleam@list:try_each(
To,
fun(Addr) ->
gleam@result:'try'(
send_command(
erlang:element(2, Client),
erlang:element(4, Client),
<<<<"RCPT TO:<"/utf8,
Addr/binary>>/binary,
">"/utf8>>,
30000
),
fun(_) ->
gleam@result:'try'(
read_response(
erlang:element(
2,
Client
),
erlang:element(
4,
Client
),
30000
),
fun(Response@1) ->
check_response_code_250_251(
Response@1
)
end
)
end
)
end
),
fun(_) ->
gleam@result:'try'(
send_command(
erlang:element(2, Client),
erlang:element(4, Client),
<<"DATA"/utf8>>,
30000
),
fun(_) ->
gleam@result:'try'(
read_response(
erlang:element(2, Client),
erlang:element(4, Client),
30000
),
fun(Response@2) ->
gleam@result:'try'(
check_response_code(
Response@2,
354
),
fun(_) ->
gleam@result:'try'(
send_data(
erlang:element(
2,
Client
),
erlang:element(
4,
Client
),
Data,
30000
),
fun(_) ->
gleam@result:'try'(
send_command(
erlang:element(
2,
Client
),
erlang:element(
4,
Client
),
<<"."/utf8>>,
60000
),
fun(_) ->
gleam@result:'try'(
read_response(
erlang:element(
2,
Client
),
erlang:element(
4,
Client
),
60000
),
fun(
Response@3
) ->
gleam@result:'try'(
check_response_code(
Response@3,
250
),
fun(
_
) ->
{ok,
nil}
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/lumenmail/smtp.gleam", 641).
-spec close_socket(lumenmail@network:socket(), boolean()) -> nil.
close_socket(Socket, Is_tls) ->
lumenmail@network:close_socket(Socket, Is_tls).
-file("src/lumenmail/smtp.gleam", 332).
?DOC(" Closes the SMTP connection gracefully.\n").
-spec close(smtp_client()) -> {ok, nil} | {error, lumenmail@types:smtp_error()}.
close(Client) ->
_ = begin
_pipe = send_command(
erlang:element(2, Client),
erlang:element(4, Client),
<<"QUIT"/utf8>>,
5000
),
gleam@result:'try'(
_pipe,
fun(_) ->
read_response(
erlang:element(2, Client),
erlang:element(4, Client),
5000
)
end
)
end,
close_socket(erlang:element(2, Client), erlang:element(4, Client)),
{ok, nil}.
-file("src/lumenmail/smtp.gleam", 344).
?DOC(" Resets the connection state (for sending multiple emails).\n").
-spec reset(smtp_client()) -> {ok, nil} | {error, lumenmail@types:smtp_error()}.
reset(Client) ->
gleam@result:'try'(
send_command(
erlang:element(2, Client),
erlang:element(4, Client),
<<"RSET"/utf8>>,
30000
),
fun(_) ->
gleam@result:'try'(
read_response(
erlang:element(2, Client),
erlang:element(4, Client),
30000
),
fun(Response) -> check_response_code(Response, 250) end
)
end
).
-file("src/lumenmail/smtp.gleam", 351).
?DOC(" Sends a NOOP command to keep connection alive.\n").
-spec noop(smtp_client()) -> {ok, nil} | {error, lumenmail@types:smtp_error()}.
noop(Client) ->
gleam@result:'try'(
send_command(
erlang:element(2, Client),
erlang:element(4, Client),
<<"NOOP"/utf8>>,
30000
),
fun(_) ->
gleam@result:'try'(
read_response(
erlang:element(2, Client),
erlang:element(4, Client),
30000
),
fun(Response) -> check_response_code(Response, 250) end
)
end
).
-file("src/lumenmail/smtp.gleam", 358).
?DOC(" Gets server capabilities.\n").
-spec capabilities(smtp_client()) -> lumenmail@types:server_capabilities().
capabilities(Client) ->
erlang:element(3, Client).