Skip to content

Commit 013ea71

Browse files
Handle already_ssl error tuple when socket is in SSL mode
Trace to include line number where trace line occurs
1 parent aa45101 commit 013ea71

1 file changed

Lines changed: 41 additions & 34 deletions

File tree

‎src/gen_smtp_client.erl‎

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
"XOAUTH2"
4949
]).
5050

51+
-define(TRACE(Options, Format, Args)
52+
,trace(Options, [$~, $p, $:, $\s | Format], [?LINE | Args])
53+
).
54+
5155
-define(TIMEOUT, 1200000).
5256

5357
-ifdef(TEST).
@@ -232,7 +236,7 @@ open(Options) ->
232236
_ ->
233237
smtp_util:mxlookup(RelayDomain)
234238
end,
235-
trace(Options, "MX records for ~s are ~p~n", [RelayDomain, MXRecords]),
239+
?TRACE(Options, "MX records for ~s are ~p~n", [RelayDomain, MXRecords]),
236240
Hosts =
237241
case MXRecords of
238242
[] ->
@@ -299,8 +303,8 @@ send_it(Email, Options) ->
299303
Domain ->
300304
[{Domain, smtp_util:mxlookup(Domain)}]
301305
end,
302-
[trace(Options, "MX records for ~s are ~p~n", [RelayDomain, MXRecords])
303-
|| {RelayDomain, MXRecords} <- Relay
306+
[?TRACE(Options, "MX records for ~s are ~p~n", [RelayDomain, MXRecords])
307+
|| {RelayDomain, MXRecords} <- Relay
304308
],
305309
Hosts = lists:flatmap(fun({_, MXRecords}) -> MXRecords end, Relay),
306310
case try_smtp_sessions(Hosts, Options, []) of
@@ -375,31 +379,31 @@ fetch_next_host(Retries, RetryCount, [{_Distance, Host} | Tail], RetryList, Opti
375379
is_integer(RetryCount), RetryCount >= Retries
376380
->
377381
% out of chances
378-
trace(Options, "retries for ~s exceeded (~p of ~p)~n", [Host, RetryCount, Retries]),
382+
?TRACE(Options, "retries for ~s exceeded (~p of ~p)~n", [Host, RetryCount, Retries]),
379383
{Tail, lists:keydelete(Host, 1, RetryList)};
380384
fetch_next_host(Retries, RetryCount, [{Distance, Host} | Tail], RetryList, Options) when
381385
is_integer(RetryCount)
382386
->
383-
trace(Options, "scheduling ~s for retry (~p of ~p)~n", [Host, RetryCount, Retries]),
387+
?TRACE(Options, "scheduling ~s for retry (~p of ~p)~n", [Host, RetryCount, Retries]),
384388
{Tail ++ [{Distance, Host}], lists:keydelete(Host, 1, RetryList) ++ [{Host, RetryCount + 1}]};
385389
fetch_next_host(0, _RetryCount, [{_Distance, Host} | Tail], RetryList, _Options) ->
386390
% done retrying completely
387391
{Tail, lists:keydelete(Host, 1, RetryList)};
388392
fetch_next_host(Retries, _RetryCount, [{Distance, Host} | Tail], RetryList, Options) ->
389393
% otherwise...
390-
trace(Options, "scheduling ~s for retry (~p of ~p)~n", [Host, 1, Retries]),
394+
?TRACE(Options, "scheduling ~s for retry (~p of ~p)~n", [Host, 1, Retries]),
391395
{Tail ++ [{Distance, Host}], lists:keydelete(Host, 1, RetryList) ++ [{Host, 1}]}.
392396

393397
-spec open_smtp_session(Host :: string(), Options :: options()) -> smtp_client_socket().
394398
open_smtp_session(Host, Options) ->
395399
{ok, Socket, _Host2, Banner} = connect(Host, Options),
396-
trace(Options, "connected to ~s; banner was ~s~n", [Host, Banner]),
400+
?TRACE(Options, "connected to ~s; banner was ~s~n", [Host, Banner]),
397401
{ok, Extensions} = try_EHLO(Socket, Options),
398-
trace(Options, "Extensions are ~p~n", [Extensions]),
402+
?TRACE(Options, "Extensions are ~p~n", [Extensions]),
399403
{Socket2, Extensions2} = try_STARTTLS(Socket, Options, Extensions),
400-
trace(Options, "Extensions are ~p~n", [Extensions2]),
404+
?TRACE(Options, "Extensions are ~p~n", [Extensions2]),
401405
Authed = try_AUTH(Socket2, Options, proplists:get_value(<<"AUTH">>, Extensions2)),
402-
trace(Options, "Authentication status is ~p~n", [Authed]),
406+
?TRACE(Options, "Authentication status is ~p~n", [Authed]),
403407
#smtp_client_socket{
404408
socket = Socket2,
405409
host = Host,
@@ -443,11 +447,11 @@ try_MAIL_FROM("<" ++ _ = From, Socket, _Extensions, Options) ->
443447
quit(Socket),
444448
throw({temporary_failure, Msg});
445449
{ok, <<"5", _Rest/binary>> = Msg} when OnTxError =:= reset ->
446-
trace(Options, "Mail FROM rejected: ~p~n", [Msg]),
450+
?TRACE(Options, "Mail FROM rejected: ~p~n", [Msg]),
447451
ok = rset_or_quit(Socket),
448452
throw({permanent_failure, Msg});
449453
{ok, Msg} ->
450-
trace(Options, "Mail FROM rejected: ~p~n", [Msg]),
454+
?TRACE(Options, "Mail FROM rejected: ~p~n", [Msg]),
451455
quit(Socket),
452456
throw({permanent_failure, Msg})
453457
end;
@@ -611,7 +615,7 @@ try_AUTH(Socket, Options, AuthTypes) ->
611615
true ->
612616
Username = to_binary(proplists:get_value(username, Options)),
613617
Password = to_binary(proplists:get_value(password, Options)),
614-
trace(Options, "Auth types: ~p~n", [AuthTypes]),
618+
?TRACE(Options, "Auth types: ~p~n", [AuthTypes]),
615619
Types = re:split(AuthTypes, " ", [{return, list}, trim]),
616620
case do_AUTH(Socket, Username, Password, Types, Options) of
617621
false ->
@@ -642,9 +646,9 @@ to_binary(String) when is_list(String) -> list_to_binary(String).
642646
) -> boolean().
643647
do_AUTH(Socket, Username, Password, Types, Options) ->
644648
FixedTypes = [string:to_upper(X) || X <- Types],
645-
trace(Options, "Fixed types: ~p~n", [FixedTypes]),
649+
?TRACE(Options, "Fixed types: ~p~n", [FixedTypes]),
646650
AllowedTypes = [X || X <- ?AUTH_PREFERENCE, lists:member(X, FixedTypes)],
647-
trace(Options, "available authentication types, in order of preference: ~p~n", [AllowedTypes]),
651+
?TRACE(Options, "available authentication types, in order of preference: ~p~n", [AllowedTypes]),
648652
do_AUTH_each(Socket, Username, Password, AllowedTypes, Options).
649653

650654
-spec do_AUTH_each(
@@ -667,14 +671,14 @@ do_AUTH_each(Socket, Username, Password, ["CRAM-MD5" | Tail], Options) ->
667671
smtp_socket:send(Socket, [String, "\r\n"]),
668672
case read_possible_multiline_reply(Socket) of
669673
{ok, <<"235", _Rest/binary>>} ->
670-
trace(Options, "authentication accepted~n", []),
674+
?TRACE(Options, "authentication accepted~n", []),
671675
true;
672676
{ok, Msg} ->
673-
trace(Options, "authentication rejected: ~s~n", [Msg]),
677+
?TRACE(Options, "authentication rejected: ~s~n", [Msg]),
674678
do_AUTH_each(Socket, Username, Password, Tail, Options)
675679
end;
676680
{ok, Something} ->
677-
trace(Options, "got ~s~n", [Something]),
681+
?TRACE(Options, "got ~s~n", [Something]),
678682
do_AUTH_each(Socket, Username, Password, Tail, Options)
679683
end;
680684
do_AUTH_each(Socket, Username, Password, ["XOAUTH2" | Tail], Options) ->
@@ -692,46 +696,46 @@ do_AUTH_each(Socket, Username, Password, ["LOGIN" | Tail], Options) ->
692696
case is_auth_username_prompt(Prompt) of
693697
true ->
694698
%% base64 Username: or username:
695-
trace(Options, "username prompt~n", []),
699+
?TRACE(Options, "username prompt~n", []),
696700
U = base64:encode(Username),
697701
smtp_socket:send(Socket, [U, "\r\n"]),
698702
{ok, Prompt2} = read_possible_multiline_reply(Socket),
699703
case is_auth_password_prompt(Prompt2) of
700704
true ->
701705
%% base64 Password: or password:
702-
trace(Options, "password prompt~n", []),
706+
?TRACE(Options, "password prompt~n", []),
703707
P = base64:encode(Password),
704708
smtp_socket:send(Socket, [P, "\r\n"]),
705709
case read_possible_multiline_reply(Socket) of
706710
{ok, <<"235 ", _Rest/binary>>} ->
707-
trace(Options, "authentication accepted~n", []),
711+
?TRACE(Options, "authentication accepted~n", []),
708712
true;
709713
{ok, Msg} ->
710-
trace(Options, "password rejected: ~s", [Msg]),
714+
?TRACE(Options, "password rejected: ~s", [Msg]),
711715
do_AUTH_each(Socket, Username, Password, Tail, Options)
712716
end;
713717
false ->
714-
trace(Options, "username rejected: ~s", [Prompt2]),
718+
?TRACE(Options, "username rejected: ~s", [Prompt2]),
715719
do_AUTH_each(Socket, Username, Password, Tail, Options)
716720
end;
717721
false ->
718-
trace(Options, "got ~s~n", [Prompt]),
722+
?TRACE(Options, "got ~s~n", [Prompt]),
719723
do_AUTH_each(Socket, Username, Password, Tail, Options)
720724
end;
721725
do_AUTH_each(Socket, Username, Password, ["PLAIN" | Tail], Options) ->
722726
AuthString = base64:encode(<<0, Username/binary, 0, Password/binary>>),
723727
smtp_socket:send(Socket, ["AUTH PLAIN ", AuthString, "\r\n"]),
724728
case read_possible_multiline_reply(Socket) of
725729
{ok, <<"235", _Rest/binary>>} ->
726-
trace(Options, "authentication accepted~n", []),
730+
?TRACE(Options, "authentication accepted~n", []),
727731
true;
728732
Else ->
729733
% TODO do we need to bother trying the multi-step PLAIN?
730-
trace(Options, "authentication rejected ~p~n", [Else]),
734+
?TRACE(Options, "authentication rejected ~p~n", [Else]),
731735
do_AUTH_each(Socket, Username, Password, Tail, Options)
732736
end;
733737
do_AUTH_each(Socket, Username, Password, [Type | Tail], Options) ->
734-
trace(Options, "unsupported AUTH type ~s~n", [Type]),
738+
?TRACE(Options, "unsupported AUTH type ~s~n", [Type]),
735739
do_AUTH_each(Socket, Username, Password, Tail, Options).
736740

737741
is_auth_username_prompt(<<"334 VXNlcm5hbWU6\r\n">>) -> true;
@@ -790,24 +794,24 @@ try_HELO(Socket, Options) ->
790794
try_STARTTLS(Socket, Options, Extensions) ->
791795
case {proplists:get_value(tls, Options), proplists:get_value(<<"STARTTLS">>, Extensions)} of
792796
{Atom, true} when Atom =:= always; Atom =:= if_available ->
793-
trace(Options, "Starting TLS~n", []),
797+
?TRACE(Options, "Starting TLS~n", []),
794798
case {do_STARTTLS(Socket, Options), Atom} of
795799
{false, always} ->
796-
trace(Options, "TLS failed~n", []),
800+
?TRACE(Options, "TLS failed~n", []),
797801
quit(Socket),
798802
erlang:throw({temporary_failure, tls_failed});
799803
{false, if_available} ->
800-
trace(Options, "TLS failed~n", []),
804+
?TRACE(Options, "TLS failed~n", []),
801805
{Socket, Extensions};
802806
{{S, E}, _} ->
803-
trace(Options, "TLS started~n", []),
807+
?TRACE(Options, "TLS started~n", []),
804808
{S, E}
805809
end;
806810
{always, _} ->
807811
quit(Socket),
808812
erlang:throw({missing_requirement, tls});
809813
_ ->
810-
trace(Options, "TLS not requested ~p~n", [Options]),
814+
?TRACE(Options, "TLS not requested ~p~n", [Options]),
811815
{Socket, Extensions}
812816
end.
813817

@@ -839,8 +843,11 @@ do_STARTTLS(Socket, Options) ->
839843
quit(Socket),
840844
error_logger:error_msg("SSL not started.~n"),
841845
erlang:throw({permanent_failure, ssl_not_started});
846+
{error, already_ssl} ->
847+
{ok, Extensions} = try_EHLO(Socket, Options),
848+
{Socket, Extensions};
842849
Else ->
843-
trace(Options, "~p~n", [Else]),
850+
?TRACE(Options, "~p~n", [Else]),
844851
false
845852
end;
846853
{ok, <<"4", _Rest/binary>> = Msg} ->
@@ -999,7 +1006,7 @@ parse_extensions(Reply, Options) ->
9991006
0 ->
10001007
{binstr:to_upper(Body), true};
10011008
_ ->
1002-
trace(Options, "discarding option ~p~n", [Body]),
1009+
?TRACE(Options, "discarding option ~p~n", [Body]),
10031010
[]
10041011
end
10051012
end

0 commit comments

Comments
 (0)