Current section
Files
Jump to
Current section
Files
src/smtp.erl
-module(smtp).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([send/4]).
-export_type([error/0]).
-type error() :: {tcp_error, mug:error()} |
{invalid_server_code_error, binary(), integer()}.
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 16).
-spec tcp_connect(binary(), integer(), integer()) -> {ok, mug:socket()} |
{error, error()}.
tcp_connect(Host, Port, Timeout) ->
_pipe = mug:new(Host, Port),
_pipe@1 = mug:timeout(_pipe, Timeout),
_pipe@2 = mug:connect(_pipe@1),
gleam@result:map_error(_pipe@2, fun(Field@0) -> {tcp_error, Field@0} end).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 23).
-spec tcp_send(mug:socket(), binary()) -> {ok, nil} | {error, error()}.
tcp_send(Socket, Message) ->
logging:log(debug, <<"CLIENT: "/utf8, Message/binary>>),
_pipe = mug:send(Socket, <<Message/binary>>),
gleam@result:map_error(_pipe, fun(Field@0) -> {tcp_error, Field@0} end).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 30).
-spec tcp_receive(mug:socket(), integer()) -> {ok, bitstring()} |
{error, error()}.
tcp_receive(Socket, Timeout) ->
gleam@result:'try'(
begin
_pipe = mug:'receive'(Socket, Timeout),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {tcp_error, Field@0} end
)
end,
fun(Packet) ->
_assert_subject = gleam@bit_array:to_string(Packet),
{ok, Packet_str} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail,
module => <<"smtp"/utf8>>,
function => <<"tcp_receive"/utf8>>,
line => 36})
end,
logging:log(debug, <<"SERVER: "/utf8, Packet_str/binary>>),
{ok, Packet}
end
).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 42).
-spec check_server_code(integer(), bitstring()) -> {ok, nil} | {error, error()}.
check_server_code(Expected_code, Response) ->
_assert_subject = gleam@bit_array:to_string(Response),
{ok, Response_str} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail,
module => <<"smtp"/utf8>>,
function => <<"check_server_code"/utf8>>,
line => 43})
end,
Server_code = gleam@string:slice(Response_str, 0, 3),
gleam@result:'try'(
begin
_pipe = gleam_stdlib:parse_int(Server_code),
gleam@result:map_error(
_pipe,
fun(_) ->
{invalid_server_code_error, Server_code, Expected_code}
end
)
end,
fun(Parsed_code) -> case Parsed_code =:= Expected_code of
true ->
{ok, nil};
false ->
{error,
{invalid_server_code_error, Server_code, Expected_code}}
end end
).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 60).
-spec cmd(mug:socket(), integer(), binary()) -> {ok, bitstring()} |
{error, error()}.
cmd(Socket, Expected_code, Message) ->
gleam@result:'try'(
tcp_send(Socket, <<Message/binary, "\r\n"/utf8>>),
fun(_) ->
gleam@result:'try'(
tcp_receive(Socket, 500),
fun(Packet) ->
gleam@result:'try'(
check_server_code(Expected_code, Packet),
fun(_) -> {ok, Packet} end
)
end
)
end
).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 70).
-spec opening_message(mug:socket(), integer()) -> {ok, nil} | {error, error()}.
opening_message(Socket, Timeout) ->
gleam@result:'try'(
tcp_receive(Socket, Timeout),
fun(Packet) -> check_server_code(220, Packet) end
).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 75).
-spec find_extension(bitstring()) -> {ok, list(binary())} | {error, any()}.
find_extension(Response) ->
Try_split_once = fun(Extension, Delimiter) ->
_pipe = gleam@string:split_once(Extension, Delimiter),
gleam@result:map(
_pipe,
fun(Extension@1) -> erlang:element(2, Extension@1) end
)
end,
_assert_subject = gleam@bit_array:to_string(Response),
{ok, Response_str} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail,
module => <<"smtp"/utf8>>,
function => <<"find_extension"/utf8>>,
line => 81})
end,
Extensions = begin
_pipe@1 = gleam@string:split(Response_str, <<"\n"/utf8>>),
_pipe@2 = gleam@list:drop(_pipe@1, 1),
_pipe@4 = gleam@list:map(
_pipe@2,
fun(Extension@2) ->
_pipe@3 = Try_split_once(Extension@2, <<"-"/utf8>>),
gleam@result:'or'(
_pipe@3,
Try_split_once(Extension@2, <<" "/utf8>>)
)
end
),
gleam@list:filter_map(_pipe@4, fun(X) -> X end)
end,
{ok, Extensions}.
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 95).
-spec has_extension(list(binary()), binary()) -> boolean().
has_extension(Extensions, Search) ->
_pipe = gleam@list:find(
Extensions,
fun(Extension) -> gleam_stdlib:string_starts_with(Extension, Search) end
),
gleam@result:is_ok(_pipe).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 112).
-spec helo(mug:socket(), binary()) -> {ok, bitstring()} | {error, error()}.
helo(Socket, Host) ->
cmd(Socket, 250, <<"HELO "/utf8, Host/binary>>).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 100).
-spec ehlo(mug:socket(), binary()) -> {ok, list(binary())} | {error, error()}.
ehlo(Socket, Host) ->
case cmd(Socket, 250, <<"EHLO "/utf8, Host/binary>>) of
{ok, Response} ->
find_extension(Response);
{error, _} ->
_pipe = helo(Socket, Host),
gleam@result:map(_pipe, fun(_) -> [] end)
end.
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 116).
-spec auth_plain(
mug:socket(),
list(binary()),
gleam@option:option({binary(), binary()})
) -> {ok, bitstring()} | {error, error()}.
auth_plain(Socket, Extensions, Auth) ->
Has_auth_extension = has_extension(Extensions, <<"AUTH"/utf8>>),
case {Has_auth_extension, Auth} of
{true, {some, {User, Password}}} ->
User_and_password = <<<<<<"\x{0000}"/utf8, User/binary>>/binary,
"\x{0000}"/utf8>>/binary,
Password/binary>>,
Base64_encoded = gleam_stdlib:bit_array_base64_encode(
<<User_and_password/binary>>,
true
),
cmd(Socket, 235, <<"AUTH PLAIN "/utf8, Base64_encoded/binary>>);
{_, _} ->
{ok, <<>>}
end.
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 131).
-spec mail_from(mug:socket(), binary()) -> {ok, bitstring()} | {error, error()}.
mail_from(Socket, From) ->
cmd(Socket, 250, <<<<"MAIL FROM:<"/utf8, From/binary>>/binary, ">"/utf8>>).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 135).
-spec rcpt_to(mug:socket(), list(binary())) -> {ok, bitstring()} |
{error, error()}.
rcpt_to(Socket, To) ->
cmd(
Socket,
250,
<<<<"RCPT TO:<"/utf8, (gleam@string:join(To, <<","/utf8>>))/binary>>/binary,
">"/utf8>>
).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 139).
-spec data(mug:socket()) -> {ok, bitstring()} | {error, error()}.
data(Socket) ->
cmd(Socket, 354, <<"DATA"/utf8>>).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 143).
-spec data_body(mug:socket(), builder:message()) -> {ok, bitstring()} |
{error, error()}.
data_body(Socket, Message) ->
Body = begin
_pipe = <<""/utf8>>,
_pipe@1 = gleam@string:append(
_pipe,
<<<<"Subject: "/utf8, (erlang:element(4, Message))/binary>>/binary,
"\r\n"/utf8>>
),
_pipe@2 = gleam@string:append(
_pipe@1,
<<<<"From: "/utf8, (erlang:element(2, Message))/binary>>/binary,
"\r\n"/utf8>>
),
_pipe@3 = gleam@string:append(
_pipe@2,
<<<<"To: "/utf8,
(gleam@string:join(erlang:element(3, Message), <<","/utf8>>))/binary>>/binary,
"\r\n"/utf8>>
),
_pipe@4 = gleam@string:append(_pipe@3, <<"\r\n"/utf8>>),
_pipe@5 = gleam@string:append(_pipe@4, erlang:element(5, Message)),
gleam@string:append(_pipe@5, <<"\r\n."/utf8>>)
end,
cmd(Socket, 250, Body).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 156).
-spec quit(mug:socket()) -> {ok, bitstring()} | {error, error()}.
quit(Socket) ->
cmd(Socket, 221, <<"QUIT"/utf8>>).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 160).
-spec log_message(binary(), integer(), builder:message()) -> nil.
log_message(Host, Port, Message) ->
logging:log(debug, <<"SENDING MAIL"/utf8>>),
logging:log(
debug,
<<<<<<"HOST: "/utf8, Host/binary>>/binary, ":"/utf8>>/binary,
(begin
_pipe = Port,
erlang:integer_to_binary(_pipe)
end)/binary>>
),
logging:log(debug, <<"FROM: "/utf8, (erlang:element(2, Message))/binary>>),
logging:log(
debug,
<<"TO: "/utf8,
(gleam@string:join(erlang:element(3, Message), <<", "/utf8>>))/binary>>
),
logging:log(
debug,
<<"SUBJECT: "/utf8, (erlang:element(4, Message))/binary>>
),
logging:log(debug, <<"BODY: "/utf8, (erlang:element(5, Message))/binary>>).
-file("/home/andre/Code/gleam/gsmtp/src/smtp.gleam", 169).
-spec send(
binary(),
integer(),
gleam@option:option({binary(), binary()}),
builder:message()
) -> {ok, nil} | {error, error()}.
send(Host, Port, Auth, Message) ->
log_message(Host, Port, Message),
Timeout = 500,
gleam@result:'try'(
tcp_connect(Host, Port, Timeout),
fun(Socket) ->
gleam@result:'try'(
opening_message(Socket, Timeout),
fun(_) ->
gleam@result:'try'(
ehlo(Socket, <<"localhost"/utf8>>),
fun(Extensions) ->
gleam@result:'try'(
auth_plain(Socket, Extensions, Auth),
fun(_) ->
gleam@result:'try'(
mail_from(
Socket,
erlang:element(2, Message)
),
fun(_) ->
gleam@result:'try'(
rcpt_to(
Socket,
erlang:element(3, Message)
),
fun(_) ->
gleam@result:'try'(
data(Socket),
fun(_) ->
gleam@result:'try'(
data_body(
Socket,
Message
),
fun(_) ->
gleam@result:'try'(
quit(
Socket
),
fun(_) ->
{ok,
nil}
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
).