-module(internal@protocol).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/internal/protocol.gleam").
-export([start_session/2]).
-export_type([sasl_mechanism/0, extensions/0, argument_error/0, protocol_error/0, action/0, protocol_config/0, receiver/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(" Implementation of https://www.rfc-editor.org/rfc/rfc5321.html\n").
-type sasl_mechanism() :: cram_md5 | login | plain | x_oauth2.
-type extensions() :: {extensions,
integer(),
internal@encoder@encoding:encoding_mode(),
internal@encoder@encoding:encoding_mode(),
boolean(),
list(sasl_mechanism())}.
-type argument_error() :: {encoding_error,
internal@encoder@encoding:encoder_error()} |
{invalid_address, binary()} |
no_from_mailbox_specified |
no_recipients_specified |
{data_size_error, integer(), integer()} |
{data_render_error, internal@renderer@internet_message:render_error()}.
-type protocol_error() :: {invalid_response, binary()} |
{unexpected_response_code, integer(), integer()} |
{invalid_request_error, argument_error()}.
-type action() :: {send,
binary(),
fun(() -> {ok, action()} | {error, protocol_error()})} |
{'receive', fun((binary()) -> {ok, action()} | {error, protocol_error()})} |
{upgrade, fun(() -> {ok, action()} | {error, protocol_error()})} |
done.
-type protocol_config() :: {protocol_config,
binary(),
gleam@option:option({binary(), binary()})}.
-type receiver() :: {receiver,
integer(),
fun((list(binary())) -> {ok, list(binary())} | {error, protocol_error()}),
fun((protocol_error()) -> {ok, action()} | {error, protocol_error()})}.
-file("src/internal/protocol.gleam", 577).
-spec 'receive'(integer()) -> receiver().
'receive'(Expected_code) ->
{receiver,
Expected_code,
fun(Field@0) -> {ok, Field@0} end,
fun(Field@0) -> {error, Field@0} end}.
-file("src/internal/protocol.gleam", 628).
-spec parse_response(binary()) -> {ok, {boolean(), integer(), binary()}} |
{error, protocol_error()}.
parse_response(Response) ->
_pipe@2 = begin
gleam@result:'try'(
gleam_stdlib:parse_int(gleam@string:slice(Response, 0, 3)),
fun(Response_code) ->
gleam@result:map(case gleam@string:slice(Response, 3, 1) of
<<" "/utf8>> ->
{ok, false};
<<""/utf8>> ->
{ok, false};
<<"-"/utf8>> ->
{ok, true};
_ ->
{error, nil}
end, fun(Need_more) ->
{Need_more,
Response_code,
begin
_pipe = Response,
_pipe@1 = gleam@string:drop_start(_pipe, 4),
gleam_stdlib:string_remove_suffix(
_pipe@1,
<<"\r\n"/utf8>>
)
end}
end)
end
)
end,
gleam@result:replace_error(_pipe@2, {invalid_response, Response}).
-file("src/internal/protocol.gleam", 581).
-spec do_receive(
integer(),
{ok, list(binary())} | {error, protocol_error()},
fun((list(binary())) -> {ok, list(binary())} | {error, protocol_error()}),
fun((protocol_error()) -> {ok, action()} | {error, protocol_error()}),
fun((list(binary())) -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
do_receive(Expected_code, Accumulator, Mapper, Recover, Next) ->
Callback = fun(Response) -> case {parse_response(Response), Accumulator} of
{{ok, {true, _, _}}, {error, _} = Error} ->
do_receive(Expected_code, Error, Mapper, Recover, Next);
{{ok, {false, _, _}}, {error, Error@1}} ->
Recover(Error@1);
{{ok, {true, Response_code, _}}, {ok, _}} when Response_code =/= Expected_code ->
do_receive(
Expected_code,
{error,
{unexpected_response_code, Expected_code, Response_code}},
Mapper,
Recover,
Next
);
{{ok, {false, Response_code@1, _}}, {ok, _}} when Response_code@1 =/= Expected_code ->
Recover(
{unexpected_response_code, Expected_code, Response_code@1}
);
{{ok, {true, _, Message}}, {ok, Messages}} ->
do_receive(
Expected_code,
{ok, [Message | Messages]},
Mapper,
Recover,
Next
);
{{ok, {false, _, Message@1}}, {ok, Messages@1}} ->
_pipe = lists:reverse([Message@1 | Messages@1]),
_pipe@1 = Mapper(_pipe),
gleam@result:'try'(_pipe@1, Next);
{{error, Error@2}, _} ->
Recover(Error@2)
end end,
{ok, {'receive', Callback}}.
-file("src/internal/protocol.gleam", 531).
-spec ignore(receiver(), fun(() -> {ok, action()} | {error, protocol_error()})) -> {ok,
action()} |
{error, protocol_error()}.
ignore(Receiver, Next) ->
do_receive(
erlang:element(2, Receiver),
{ok, []},
erlang:element(3, Receiver),
erlang:element(4, Receiver),
fun(_) -> Next() end
).
-file("src/internal/protocol.gleam", 524).
-spec send(binary(), fun(() -> {ok, action()} | {error, protocol_error()})) -> {ok,
action()} |
{error, protocol_error()}.
send(Command, Next) ->
{ok, {send, <<Command/binary, "\r\n"/utf8>>, Next}}.
-file("src/internal/protocol.gleam", 512).
-spec quit() -> {ok, action()} | {error, protocol_error()}.
quit() ->
send(
<<"QUIT"/utf8>>,
fun() -> ignore('receive'(221), fun() -> {ok, done} end) end
).
-file("src/internal/protocol.gleam", 495).
-spec do_mail_data(
list(binary()),
fun(() -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
do_mail_data(Data, Next) ->
case Data of
[Data@1 | Rest_data] ->
Data@2 = case gleam_stdlib:string_starts_with(Data@1, <<"."/utf8>>) of
true ->
<<"."/utf8, Data@1/binary>>;
false ->
Data@1
end,
send(Data@2, fun() -> do_mail_data(Rest_data, Next) end);
[] ->
Next()
end.
-file("src/internal/protocol.gleam", 464).
-spec mail_data(
sendr@message:message(),
extensions(),
fun(() -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
mail_data(Message, Extensions, Next) ->
Data = begin
_pipe = Message,
_pipe@1 = internal@renderer@internet_message:encode(
_pipe,
erlang:element(4, Extensions)
),
_pipe@2 = gleam@result:map_error(
_pipe@1,
fun(Field@0) -> {data_render_error, Field@0} end
),
_pipe@3 = gleam@result:'try'(
_pipe@2,
fun(Body) ->
Body_size = gleam@list:fold(
Body,
0,
fun(Accumulator, Line) ->
Accumulator + erlang:byte_size(Line)
end
),
case erlang:element(2, Extensions) of
Size when (Size =:= 0) orelse (Body_size =< Size) ->
{ok, Body};
Size@1 ->
{error, {data_size_error, Body_size, Size@1}}
end
end
),
gleam@result:map_error(
_pipe@3,
fun(Field@0) -> {invalid_request_error, Field@0} end
)
end,
gleam@result:'try'(
Data,
fun(Data@1) ->
send(
<<"DATA"/utf8>>,
fun() ->
ignore(
'receive'(354),
fun() ->
do_mail_data(
Data@1,
fun() ->
send(
<<"."/utf8>>,
fun() ->
ignore(
'receive'(250),
fun() -> Next() end
)
end
)
end
)
end
)
end
)
end
).
-file("src/internal/protocol.gleam", 450).
-spec do_mail_recipients(
list(binary()),
fun(() -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
do_mail_recipients(Addresses, Next) ->
case Addresses of
[Address | Rest_addresses] ->
send(
<<<<"RCPT TO:<"/utf8, Address/binary>>/binary, ">"/utf8>>,
fun() ->
ignore(
'receive'(250),
fun() -> do_mail_recipients(Rest_addresses, Next) end
)
end
);
[] ->
Next()
end.
-file("src/internal/protocol.gleam", 664).
-spec is_ascii_string(binary()) -> boolean().
is_ascii_string(String) ->
_pipe = String,
_pipe@1 = gleam@string:split(_pipe, <<""/utf8>>),
gleam@list:all(
_pipe@1,
fun(Char) ->
(gleam@string:compare(Char, <<"\x{1f}"/utf8>>) =:= gt) andalso (gleam@string:compare(
Char,
<<"\x{7f}"/utf8>>
)
=:= lt)
end
).
-file("src/internal/protocol.gleam", 647).
-spec validate_mailbox(sendr@message@mailbox:mailbox(), extensions()) -> {ok,
sendr@message@mailbox:mailbox()} |
{error, argument_error()}.
validate_mailbox(Mailbox, Extensions) ->
case gleam@string:split_once(erlang:element(3, Mailbox), <<"@"/utf8>>) of
{ok, {Local, _}} ->
case (erlang:element(3, Extensions) =:= utf8) orelse is_ascii_string(
Local
) of
true ->
{ok, Mailbox};
false ->
{error, {invalid_address, erlang:element(3, Mailbox)}}
end;
{error, _} ->
{error, {invalid_address, erlang:element(3, Mailbox)}}
end.
-file("src/internal/protocol.gleam", 362).
-spec recipients(sendr@message:message()) -> list(sendr@message@mailbox:mailbox()).
recipients(Message) ->
_pipe = [erlang:element(4, Message),
erlang:element(5, Message),
erlang:element(6, Message)],
_pipe@1 = gleam@option:values(_pipe),
lists:append(_pipe@1).
-file("src/internal/protocol.gleam", 418).
-spec mail_recipients(
sendr@message:message(),
extensions(),
fun(() -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
mail_recipients(Message, Extensions, Next) ->
Addresses = begin
_pipe = Message,
_pipe@1 = recipients(_pipe),
_pipe@6 = gleam@list:map(_pipe@1, fun(Mailbox) -> _pipe@2 = Mailbox,
_pipe@3 = validate_mailbox(_pipe@2, Extensions),
_pipe@5 = gleam@result:'try'(
_pipe@3,
fun(Mailbox@1) ->
_pipe@4 = internal@encoder@idna:encode_email_address(
erlang:element(3, Mailbox@1),
erlang:element(3, Extensions)
),
gleam@result:map_error(
_pipe@4,
fun(Field@0) -> {encoding_error, Field@0} end
)
end
),
gleam@result:map_error(
_pipe@5,
fun(Field@0) -> {invalid_request_error, Field@0} end
) end),
_pipe@7 = gleam@result:all(_pipe@6),
gleam@result:'try'(_pipe@7, fun(Recipients) -> case Recipients of
[] ->
{error,
{invalid_request_error, no_recipients_specified}};
_ ->
{ok, Recipients}
end end)
end,
gleam@result:'try'(
Addresses,
fun(Addresses@1) -> do_mail_recipients(Addresses@1, Next) end
).
-file("src/internal/protocol.gleam", 379).
-spec mail_from(
sendr@message:message(),
extensions(),
fun(() -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
mail_from(Message, Extensions, Next) ->
Smtp_utf8 = case erlang:element(3, Extensions) of
ascii ->
none;
utf8 ->
{some, <<"SMTPUTF8"/utf8>>}
end,
Body_8bitmime = case erlang:element(4, Extensions) of
ascii ->
none;
utf8 ->
{some, <<"BODY=8BITMIME"/utf8>>}
end,
Address@1 = begin
_pipe = erlang:element(2, Message),
_pipe@1 = gleam@option:to_result(_pipe, no_from_mailbox_specified),
_pipe@2 = gleam@result:'try'(
_pipe@1,
fun(_capture) -> validate_mailbox(_capture, Extensions) end
),
_pipe@7 = gleam@result:'try'(
_pipe@2,
fun(Mailbox) ->
_pipe@3 = internal@encoder@idna:encode_email_address(
erlang:element(3, Mailbox),
erlang:element(3, Extensions)
),
_pipe@6 = gleam@result:map(
_pipe@3,
fun(Address) ->
_pipe@4 = [{some,
<<<<"<"/utf8, Address/binary>>/binary,
">"/utf8>>},
Smtp_utf8,
Body_8bitmime],
_pipe@5 = gleam@option:values(_pipe@4),
gleam@string:join(_pipe@5, <<" "/utf8>>)
end
),
gleam@result:map_error(
_pipe@6,
fun(Field@0) -> {encoding_error, Field@0} end
)
end
),
gleam@result:map_error(
_pipe@7,
fun(Field@0) -> {invalid_request_error, Field@0} end
)
end,
gleam@result:'try'(
Address@1,
fun(Address@2) ->
send(
<<"MAIL FROM:"/utf8, Address@2/binary>>,
fun() -> ignore('receive'(250), fun() -> Next() end) end
)
end
).
-file("src/internal/protocol.gleam", 368).
-spec mail_transaction(
extensions(),
sendr@message:message(),
fun(() -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
mail_transaction(Extensions, Message, Next) ->
mail_from(
Message,
Extensions,
fun() ->
mail_recipients(
Message,
Extensions,
fun() -> mail_data(Message, Extensions, fun() -> Next() end) end
)
end
).
-file("src/internal/protocol.gleam", 285).
-spec authenticate_xoauth2(
{binary(), binary()},
fun(() -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
authenticate_xoauth2(Credentials, Next) ->
{Username, Token} = Credentials,
Encoded = gleam_stdlib:base64_encode(
<<"user="/utf8,
Username/binary,
1,
"auth=Bearer "/utf8,
Token/binary,
1,
1>>,
true
),
send(
<<"AUTH XOAUTH2 "/utf8, Encoded/binary>>,
fun() -> ignore('receive'(235), fun() -> Next() end) end
).
-file("src/internal/protocol.gleam", 272).
-spec authenticate_plain(
{binary(), binary()},
fun(() -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
authenticate_plain(Credentials, Next) ->
{Username, Password} = Credentials,
Encoded = gleam_stdlib:base64_encode(
<<0, Username/binary, 0, Password/binary>>,
true
),
send(
<<"AUTH PLAIN "/utf8, Encoded/binary>>,
fun() -> ignore('receive'(235), fun() -> Next() end) end
).
-file("src/internal/protocol.gleam", 544).
-spec check(
receiver(),
list(binary()),
fun(() -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
check(Receiver, Valid_responses, Next) ->
Mapper = fun(Response) ->
case gleam@list:all(
Response,
fun(_capture) -> gleam@list:contains(Valid_responses, _capture) end
) of
false ->
{error,
{invalid_response,
gleam@string:join(Response, <<"\n"/utf8>>)}};
true ->
{ok, Response}
end
end,
do_receive(
erlang:element(2, Receiver),
{ok, []},
Mapper,
erlang:element(4, Receiver),
fun(_) -> Next() end
).
-file("src/internal/protocol.gleam", 255).
-spec authenticate_login(
{binary(), binary()},
fun(() -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
authenticate_login(Credentials, Next) ->
{Username, Password} = Credentials,
Encoded_username = gleam_stdlib:base64_encode(<<Username/binary>>, true),
Encoded_password = gleam_stdlib:base64_encode(<<Password/binary>>, true),
send(
<<"AUTH LOGIN"/utf8>>,
fun() ->
check(
'receive'(334),
[<<"VXNlcm5hbWU6"/utf8>>, <<"dXNlcm5hbWU6"/utf8>>],
fun() ->
send(
Encoded_username,
fun() ->
check(
'receive'(334),
[<<"UGFzc3dvcmQ6"/utf8>>,
<<"cGFzc3dvcmQ6"/utf8>>],
fun() ->
send(
Encoded_password,
fun() ->
ignore(
'receive'(235),
fun() -> Next() end
)
end
)
end
)
end
)
end
)
end
).
-file("src/internal/protocol.gleam", 561).
-spec map(
receiver(),
fun((list(binary())) -> {ok, list(binary())} | {error, protocol_error()}),
fun((list(binary())) -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
map(Receiver, Mapper, Next) ->
do_receive(
erlang:element(2, Receiver),
{ok, []},
Mapper,
erlang:element(4, Receiver),
Next
).
-file("src/internal/protocol.gleam", 226).
-spec authenticate_cram_md5(
{binary(), binary()},
fun(() -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
authenticate_cram_md5(Credentials, Next) ->
{Username, Password} = Credentials,
Encode_credentials = fun(Challenge) ->
Challenge@1 = gleam@string:join(Challenge, <<""/utf8>>),
_pipe = Challenge@1,
_pipe@1 = gleam@bit_array:base64_decode(_pipe),
_pipe@2 = gleam@result:map(
_pipe@1,
fun(_capture) ->
gleam_crypto_ffi:hmac(_capture, md5, <<Password/binary>>)
end
),
_pipe@3 = gleam@result:map(_pipe@2, fun gleam_stdlib:base16_encode/1),
_pipe@4 = gleam@result:map(_pipe@3, fun string:lowercase/1),
_pipe@5 = gleam@result:map(
_pipe@4,
fun(_capture@1) ->
gleam@string:append(<<Username/binary, " "/utf8>>, _capture@1)
end
),
_pipe@6 = gleam@result:map(
_pipe@5,
fun(Response) ->
gleam_stdlib:base64_encode(<<Response/binary>>, true)
end
),
_pipe@7 = gleam@result:map(
_pipe@6,
fun(Encoded_credentials) -> [Encoded_credentials] end
),
gleam@result:replace_error(_pipe@7, {invalid_response, Challenge@1})
end,
send(
<<"AUTH CRAM-MD5"/utf8>>,
fun() ->
map(
'receive'(334),
Encode_credentials,
fun(Encoded_credentials@1) ->
send(
gleam@string:join(Encoded_credentials@1, <<""/utf8>>),
fun() -> ignore('receive'(235), fun() -> Next() end) end
)
end
)
end
).
-file("src/internal/protocol.gleam", 212).
-spec authenticate(
extensions(),
gleam@option:option({binary(), binary()}),
fun(() -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
authenticate(Extensions, Credentials, Next) ->
case {Credentials, erlang:element(6, Extensions)} of
{none, _} ->
Next();
{{some, _}, []} ->
Next();
{{some, Credentials@1}, [cram_md5 | _]} ->
authenticate_cram_md5(Credentials@1, Next);
{{some, Credentials@2}, [login | _]} ->
authenticate_login(Credentials@2, Next);
{{some, Credentials@3}, [plain | _]} ->
authenticate_plain(Credentials@3, Next);
{{some, Credentials@4}, [x_oauth2 | _]} ->
authenticate_xoauth2(Credentials@4, Next)
end.
-file("src/internal/protocol.gleam", 518).
-spec upgrade(fun(() -> {ok, action()} | {error, protocol_error()})) -> {ok,
action()} |
{error, protocol_error()}.
upgrade(Next) ->
{ok, {upgrade, Next}}.
-file("src/internal/protocol.gleam", 194).
-spec start_tls(
extensions(),
fun((fun((extensions()) -> {ok, action()} | {error, protocol_error()})) -> {ok,
action()} |
{error, protocol_error()}),
fun((extensions()) -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
start_tls(Extensions, Initiate_client, Next) ->
case erlang:element(5, Extensions) of
false ->
Next(Extensions);
true ->
send(
<<"STARTTLS"/utf8>>,
fun() ->
ignore(
'receive'(220),
fun() ->
upgrade(
fun() ->
Initiate_client(
fun(Extensions@1) ->
Next(Extensions@1)
end
)
end
)
end
)
end
)
end.
-file("src/internal/protocol.gleam", 159).
-spec initiate_server(fun(() -> {ok, action()} | {error, protocol_error()})) -> {ok,
action()} |
{error, protocol_error()}.
initiate_server(Next) ->
ignore('receive'(220), fun() -> Next() end).
-file("src/internal/protocol.gleam", 569).
-spec recover(
receiver(),
fun((protocol_error()) -> {ok, action()} | {error, protocol_error()}),
fun((list(binary())) -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
recover(Receiver, Recover, Next) ->
do_receive(
erlang:element(2, Receiver),
{ok, []},
erlang:element(3, Receiver),
Recover,
Next
).
-file("src/internal/protocol.gleam", 301).
-spec parse_extensions(list(binary()), boolean(), boolean()) -> extensions().
parse_extensions(Extensions, Has_smtputf8, Has_8bitmime) ->
_pipe = Extensions,
_pipe@1 = gleam@list:drop(_pipe, 1),
gleam@list:fold(
_pipe@1,
{extensions, 0, ascii, ascii, false, []},
fun(Extensions@1, Line) -> case string:uppercase(Line) of
<<"8BITMIME"/utf8>> when Has_8bitmime ->
{extensions,
erlang:element(2, Extensions@1),
erlang:element(3, Extensions@1),
utf8,
erlang:element(5, Extensions@1),
erlang:element(6, Extensions@1)};
<<"AUTH "/utf8, Mechanisms/binary>> ->
_pipe@2 = Mechanisms,
_pipe@3 = gleam@string:split(_pipe@2, <<" "/utf8>>),
gleam@list:fold(
_pipe@3,
Extensions@1,
fun(Extensions@2, Mechanism) -> case Mechanism of
<<"CRAM-MD5"/utf8>> ->
{extensions,
erlang:element(2, Extensions@2),
erlang:element(3, Extensions@2),
erlang:element(4, Extensions@2),
erlang:element(5, Extensions@2),
lists:append(
erlang:element(6, Extensions@2),
[cram_md5]
)};
<<"LOGIN"/utf8>> ->
{extensions,
erlang:element(2, Extensions@2),
erlang:element(3, Extensions@2),
erlang:element(4, Extensions@2),
erlang:element(5, Extensions@2),
lists:append(
erlang:element(6, Extensions@2),
[login]
)};
<<"PLAIN"/utf8>> ->
{extensions,
erlang:element(2, Extensions@2),
erlang:element(3, Extensions@2),
erlang:element(4, Extensions@2),
erlang:element(5, Extensions@2),
lists:append(
erlang:element(6, Extensions@2),
[plain]
)};
<<"XOAUTH2"/utf8>> ->
{extensions,
erlang:element(2, Extensions@2),
erlang:element(3, Extensions@2),
erlang:element(4, Extensions@2),
erlang:element(5, Extensions@2),
lists:append(
erlang:element(6, Extensions@2),
[x_oauth2]
)};
_ ->
Extensions@2
end end
);
<<"SIZE "/utf8, Size/binary>> ->
{extensions,
gleam@result:unwrap(gleam_stdlib:parse_int(Size), 0),
erlang:element(3, Extensions@1),
erlang:element(4, Extensions@1),
erlang:element(5, Extensions@1),
erlang:element(6, Extensions@1)};
<<"SMTPUTF8"/utf8>> when Has_smtputf8 ->
{extensions,
erlang:element(2, Extensions@1),
utf8,
erlang:element(4, Extensions@1),
erlang:element(5, Extensions@1),
erlang:element(6, Extensions@1)};
<<"STARTTLS"/utf8>> ->
{extensions,
erlang:element(2, Extensions@1),
erlang:element(3, Extensions@1),
erlang:element(4, Extensions@1),
true,
erlang:element(6, Extensions@1)};
_ ->
Extensions@1
end end
).
-file("src/internal/protocol.gleam", 166).
-spec initiate_client(
binary(),
boolean(),
boolean(),
fun((extensions()) -> {ok, action()} | {error, protocol_error()})
) -> {ok, action()} | {error, protocol_error()}.
initiate_client(Helo_host, Has_smtputf8, Has_8bitmime, Next) ->
Next@1 = fun(Extensions) -> _pipe = Extensions,
_pipe@1 = parse_extensions(_pipe, Has_smtputf8, Has_8bitmime),
Next(_pipe@1) end,
send(
<<"EHLO "/utf8, Helo_host/binary>>,
fun() -> recover('receive'(250), fun(Error) -> case Error of
{unexpected_response_code, _, 500} ->
send(
<<"HELO "/utf8, Helo_host/binary>>,
fun() ->
ignore(
'receive'(250),
fun() -> Next@1([]) end
)
end
);
{unexpected_response_code, _, _} = Error@1 ->
{error, Error@1};
{invalid_response, _} = Error@1 ->
{error, Error@1};
{invalid_request_error, _} = Error@1 ->
{error, Error@1}
end end, fun(Extensions@1) -> Next@1(Extensions@1) end) end
).
-file("src/internal/protocol.gleam", 153).
-spec encode_domain(binary()) -> {ok, binary()} | {error, protocol_error()}.
encode_domain(Domain) ->
_pipe = Domain,
_pipe@1 = internal@encoder@idna:encode_domain(_pipe, ascii),
gleam@result:map_error(
_pipe@1,
fun(Error) -> {invalid_request_error, {encoding_error, Error}} end
).
-file("src/internal/protocol.gleam", 143).
-spec has_8bitmime_body(sendr@message:message()) -> {ok, boolean()} |
{error, protocol_error()}.
has_8bitmime_body(Message) ->
_pipe = gleam@option:values(
[gleam@option:map(
erlang:element(3, erlang:element(9, Message)),
fun is_ascii_string/1
),
gleam@option:map(
erlang:element(2, erlang:element(9, Message)),
fun is_ascii_string/1
)]
),
_pipe@1 = gleam@list:any(_pipe, fun gleam@function:identity/1),
_pipe@2 = gleam@bool:negate(_pipe@1),
{ok, _pipe@2}.
-file("src/internal/protocol.gleam", 127).
-spec has_smtputf8(sendr@message:message()) -> {ok, boolean()} |
{error, protocol_error()}.
has_smtputf8(Message) ->
_pipe = [gleam@option:unwrap(
erlang:element(2, Message),
{mailbox, <<""/utf8>>, <<""/utf8>>}
) |
recipients(Message)],
_pipe@3 = gleam@list:map(
_pipe,
fun(Mailbox) ->
_pipe@1 = gleam@string:split_once(
erlang:element(3, Mailbox),
<<"@"/utf8>>
),
_pipe@2 = gleam@result:map(
_pipe@1,
fun(Address) ->
{Local, _} = Address,
not is_ascii_string(Local)
end
),
gleam@result:replace_error(
_pipe@2,
{invalid_request_error,
{invalid_address, erlang:element(3, Mailbox)}}
)
end
),
_pipe@4 = gleam@result:all(_pipe@3),
gleam@result:map(
_pipe@4,
fun(_capture) ->
gleam@list:any(_capture, fun gleam@function:identity/1)
end
).
-file("src/internal/protocol.gleam", 119).
-spec has_from_and_recipients(sendr@message:message()) -> {ok, nil} |
{error, protocol_error()}.
has_from_and_recipients(Message) ->
case {erlang:element(2, Message), recipients(Message)} of
{none, _} ->
{error, {invalid_request_error, no_from_mailbox_specified}};
{_, []} ->
{error, {invalid_request_error, no_recipients_specified}};
{_, _} ->
{ok, nil}
end.
-file("src/internal/protocol.gleam", 98).
?DOC(
" Start an SMTP protocol session for sending a message.\n"
"\n"
" Executes the full SMTP session: server greeting, EHLO/HELO, STARTTLS,\n"
" authentication (if configured), and mail transaction (MAIL FROM, RCPT TO, DATA).\n"
"\n"
" - `message`: The `sendr/message.Message` to deliver.\n"
" - `config`: The `ProtocolConfig` for the session.\n"
"\n"
" Returns an `Action` representing the first action in the protocol sequence.\n"
).
-spec start_session(sendr@message:message(), protocol_config()) -> {ok,
action()} |
{error, protocol_error()}.
start_session(Message, Config) ->
gleam@result:'try'(
has_from_and_recipients(Message),
fun(_) ->
gleam@result:'try'(
has_smtputf8(Message),
fun(Has_smtputf8) ->
gleam@result:'try'(
has_8bitmime_body(Message),
fun(Has_8bitmime) ->
gleam@result:'try'(
encode_domain(erlang:element(2, Config)),
fun(Encoded_helo_host) ->
begin
Initiate_client = fun(Next) ->
initiate_client(
Encoded_helo_host,
Has_smtputf8,
Has_8bitmime,
Next
)
end,
initiate_server(
fun() ->
Initiate_client(
fun(Extensions) ->
start_tls(
Extensions,
Initiate_client,
fun(Extensions@1) ->
authenticate(
Extensions@1,
erlang:element(
3,
Config
),
fun() ->
mail_transaction(
Extensions@1,
Message,
fun(
) ->
quit(
)
end
)
end
)
end
)
end
)
end
)
end
end
)
end
)
end
)
end
).