-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, <>, 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(<>, true), Encoded_password = gleam_stdlib:base64_encode(<>, 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, <>) 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(<>, _capture@1) end ), _pipe@6 = gleam@result:map( _pipe@5, fun(Response) -> gleam_stdlib:base64_encode(<>, 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 ).