1
0
Fork 0
mirror of https://github.com/processone/ejabberd synced 2025-10-03 17:59:31 +02:00

Handle CAPTCHA forms using captcha_form codec

This commit is contained in:
Evgeny Khramtsov 2019-07-16 17:51:51 +03:00
parent 6b3d0d154e
commit f85488583c
2 changed files with 54 additions and 65 deletions

View file

@ -24,7 +24,7 @@
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.1"}}}, {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.1"}}},
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.16"}}}, {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.16"}}},
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", "7fd02f3a2f"}}, {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", "7fd02f3a2f"}},
{xmpp, ".*", {git, "https://github.com/processone/xmpp", "31413d7"}}, {xmpp, ".*", {git, "https://github.com/processone/xmpp", "3e2f1c5"}},
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.19"}}}, {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.19"}}},
{yconf, ".*", {git, "https://github.com/processone/yconf", "dfeaa7e"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", "dfeaa7e"}},
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}}, {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},

View file

@ -74,15 +74,8 @@ captcha_text(Lang) ->
-spec mk_ocr_field(binary(), binary(), binary()) -> xdata_field(). -spec mk_ocr_field(binary(), binary(), binary()) -> xdata_field().
mk_ocr_field(Lang, CID, Type) -> mk_ocr_field(Lang, CID, Type) ->
URI = #media_uri{type = Type, uri = <<"cid:", CID/binary>>}, URI = #media_uri{type = Type, uri = <<"cid:", CID/binary>>},
#xdata_field{var = <<"ocr">>, [_, F] = captcha_form:encode([{ocr, <<>>}], Lang, [ocr]),
type = 'text-single', xmpp:set_els(F, [#media{uri = [URI]}]).
label = captcha_text(Lang),
required = true,
sub_els = [#media{uri = [URI]}]}.
-spec mk_field(_, binary(), binary()) -> xdata_field().
mk_field(Type, Var, Value) ->
#xdata_field{type = Type, var = Var, values = [Value]}.
-spec create_captcha(binary(), jid(), jid(), -spec create_captcha(binary(), jid(), jid(),
binary(), any(), binary(), any(),
@ -91,30 +84,27 @@ mk_field(Type, Var, Value) ->
create_captcha(SID, From, To, Lang, Limiter, Args) -> create_captcha(SID, From, To, Lang, Limiter, Args) ->
case create_image(Limiter) of case create_image(Limiter) of
{ok, Type, Key, Image} -> {ok, Type, Key, Image} ->
Id = <<(p1_rand:get_string())/binary>>, Id = <<(p1_rand:get_string())/binary>>,
JID = jid:encode(From), JID = jid:encode(From),
CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>, CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>,
Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image},
data = Image}, Fs = captcha_form:encode(
Fs = [mk_field(hidden, <<"FORM_TYPE">>, ?NS_CAPTCHA), [{from, To}, {challenge, Id}, {sid, SID},
mk_field(hidden, <<"from">>, jid:encode(To)), mk_ocr_field(Lang, CID, Type)],
mk_field(hidden, <<"challenge">>, Id), Lang, [challenge]),
mk_field(hidden, <<"sid">>, SID), X = #xdata{type = form, fields = Fs},
mk_ocr_field(Lang, CID, Type)], Captcha = #xcaptcha{xdata = X},
X = #xdata{type = form, fields = Fs}, BodyString = {?T("Your subscription request and/or messages to ~s have been blocked. "
Captcha = #xcaptcha{xdata = X}, "To unblock your subscription request, visit ~s"), [JID, get_url(Id)]},
BodyString = {?T("Your subscription request and/or messages to ~s have been blocked. " Body = xmpp:mk_text(BodyString, Lang),
"To unblock your subscription request, visit ~s"), [JID, get_url(Id)]}, OOB = #oob_x{url = get_url(Id)},
Body = xmpp:mk_text(BodyString, Lang), Hint = #hint{type = 'no-store'},
OOB = #oob_x{url = get_url(Id)}, Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}),
Hint = #hint{type = 'no-store'}, ets:insert(captcha,
Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, #captcha{id = Id, pid = self(), key = Key, tref = Tref,
{remove_id, Id}), args = Args}),
ets:insert(captcha, {ok, Id, Body, [Hint, OOB, Captcha, Data]};
#captcha{id = Id, pid = self(), key = Key, tref = Tref, Err -> Err
args = Args}),
{ok, Id, Body, [Hint, OOB, Captcha, Data]};
Err -> Err
end. end.
-spec create_captcha_x(binary(), jid(), binary(), any(), xdata()) -> -spec create_captcha_x(binary(), jid(), binary(), any(), xdata()) ->
@ -122,32 +112,23 @@ create_captcha(SID, From, To, Lang, Limiter, Args) ->
create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) -> create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) ->
case create_image(Limiter) of case create_image(Limiter) of
{ok, Type, Key, Image} -> {ok, Type, Key, Image} ->
Id = <<(p1_rand:get_string())/binary>>, Id = <<(p1_rand:get_string())/binary>>,
CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>, CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>,
Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image}, Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image},
HelpTxt = translate:translate(Lang, HelpTxt = translate:translate(
?T("If you don't see the CAPTCHA image here, " Lang, ?T("If you don't see the CAPTCHA image here, visit the web page.")),
"visit the web page.")), Imageurl = get_url(<<Id/binary, "/image">>),
Imageurl = get_url(<<Id/binary, "/image">>), [H|T] = captcha_form:encode(
NewFs = [mk_field(hidden, <<"FORM_TYPE">>, ?NS_CAPTCHA)|Fs] ++ [{'captcha-fallback-text', HelpTxt},
[#xdata_field{type = fixed, var = <<"captcha-fallback-text">>, values = [HelpTxt]}, {'captcha-fallback-url', Imageurl},
#xdata_field{type = hidden, var = <<"captchahidden">>, {from, To}, {challenge, Id}, {sid, SID},
values = [<<"workaround-for-psi">>]}, mk_ocr_field(Lang, CID, Type)],
#xdata_field{type = 'text-single', var = <<"captcha-fallback-url">>, Lang, [challenge]),
label = translate:translate( Captcha = X#xdata{type = form, fields = [H|Fs ++ T]},
Lang, ?T("CAPTCHA web page")), Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}),
values = [Imageurl]}, ets:insert(captcha, #captcha{id = Id, key = Key, tref = Tref}),
mk_field(hidden, <<"from">>, jid:encode(To)), {ok, [Captcha, Data]};
mk_field(hidden, <<"challenge">>, Id), Err -> Err
mk_field(hidden, <<"sid">>, SID),
mk_ocr_field(Lang, CID, Type)],
Captcha = X#xdata{type = form, fields = NewFs},
Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE,
{remove_id, Id}),
ets:insert(captcha,
#captcha{id = Id, key = Key, tref = Tref}),
{ok, [Captcha, Data]};
Err -> Err
end. end.
-spec build_captcha_html(binary(), binary()) -> captcha_not_found | -spec build_captcha_html(binary(), binary()) -> captcha_not_found |
@ -201,15 +182,23 @@ build_captcha_html(Id, Lang) ->
-spec process_reply(xmpp_element()) -> ok | {error, bad_match | not_found | malformed}. -spec process_reply(xmpp_element()) -> ok | {error, bad_match | not_found | malformed}.
process_reply(#xdata{} = X) -> process_reply(#xdata{} = X) ->
case {xmpp_util:get_xdata_values(<<"challenge">>, X), Required = [<<"challenge">>, <<"ocr">>],
xmpp_util:get_xdata_values(<<"ocr">>, X)} of Fs = lists:filter(
{[Id], [OCR]} -> fun(#xdata_field{var = Var}) ->
lists:member(Var, [<<"FORM_TYPE">>|Required])
end, X#xdata.fields),
try captcha_form:decode(Fs, [?NS_CAPTCHA], Required) of
Props ->
Id = proplists:get_value(challenge, Props),
OCR = proplists:get_value(ocr, Props),
case check_captcha(Id, OCR) of case check_captcha(Id, OCR) of
captcha_valid -> ok; captcha_valid -> ok;
captcha_non_valid -> {error, bad_match}; captcha_non_valid -> {error, bad_match};
captcha_not_found -> {error, not_found} captcha_not_found -> {error, not_found}
end; end
_ -> catch _:{captcha_form, Why} ->
?WARNING_MSG("Malformed CAPTCHA form: ~s",
[captcha_form:format_error(Why)]),
{error, malformed} {error, malformed}
end; end;
process_reply(#xcaptcha{xdata = #xdata{} = X}) -> process_reply(#xcaptcha{xdata = #xdata{} = X}) ->