]> granicus.if.org Git - ejabberd/commitdiff
Handle CAPTCHA forms using captcha_form codec
authorEvgeny Khramtsov <ekhramtsov@process-one.net>
Tue, 16 Jul 2019 14:51:51 +0000 (17:51 +0300)
committerEvgeny Khramtsov <ekhramtsov@process-one.net>
Tue, 16 Jul 2019 14:51:51 +0000 (17:51 +0300)
rebar.config
src/ejabberd_captcha.erl

index 72d6c70593b6bf7ee8fc381c7164fa2e8b1ff06e..706bc6ed8723dc51f2ed842b6dc349f3a2e67c95 100644 (file)
@@ -24,7 +24,7 @@
         {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.1"}}},
         {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.16"}}},
         {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"}}},
        {yconf, ".*", {git, "https://github.com/processone/yconf", "dfeaa7e"}},
         {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
index 720f375c59334e56910764034f3a2f52e15d712c..adef28e1824c671fb6bb511fbedf37a9eca5b1a4 100644 (file)
@@ -74,15 +74,8 @@ captcha_text(Lang) ->
 -spec mk_ocr_field(binary(), binary(), binary()) -> xdata_field().
 mk_ocr_field(Lang, CID, Type) ->
     URI = #media_uri{type = Type, uri = <<"cid:", CID/binary>>},
-    #xdata_field{var = <<"ocr">>,
-                type = 'text-single',
-                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]}.
+    [_, F] = captcha_form:encode([{ocr, <<>>}], Lang, [ocr]),
+    xmpp:set_els(F, [#media{uri = [URI]}]).
 
 -spec create_captcha(binary(), jid(), jid(),
                      binary(), any(),
@@ -91,30 +84,27 @@ mk_field(Type, Var, Value) ->
 create_captcha(SID, From, To, Lang, Limiter, Args) ->
     case create_image(Limiter) of
       {ok, Type, Key, Image} ->
-         Id = <<(p1_rand:get_string())/binary>>,
-         JID = jid:encode(From),
-         CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>,
-         Data = #bob_data{cid = CID, 'max-age' = 0, type = Type,
-                          data = Image},
-         Fs = [mk_field(hidden, <<"FORM_TYPE">>, ?NS_CAPTCHA),
-               mk_field(hidden, <<"from">>, jid:encode(To)),
-               mk_field(hidden, <<"challenge">>, Id),
-               mk_field(hidden, <<"sid">>, SID),
-               mk_ocr_field(Lang, CID, Type)],
-         X = #xdata{type = form, fields = Fs},
-         Captcha = #xcaptcha{xdata = X},
-          BodyString = {?T("Your subscription request and/or messages to ~s have been blocked. "
-                          "To unblock your subscription request, visit ~s"), [JID, get_url(Id)]},
-         Body = xmpp:mk_text(BodyString, Lang),
-         OOB = #oob_x{url = get_url(Id)},
-         Hint = #hint{type = 'no-store'},
-         Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE,
-                                  {remove_id, Id}),
-         ets:insert(captcha,
-                    #captcha{id = Id, pid = self(), key = Key, tref = Tref,
-                             args = Args}),
-         {ok, Id, Body, [Hint, OOB, Captcha, Data]};
-      Err -> Err
+           Id = <<(p1_rand:get_string())/binary>>,
+           JID = jid:encode(From),
+           CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>,
+           Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image},
+           Fs = captcha_form:encode(
+                  [{from, To}, {challenge, Id}, {sid, SID},
+                   mk_ocr_field(Lang, CID, Type)],
+                  Lang, [challenge]),
+           X = #xdata{type = form, fields = Fs},
+           Captcha = #xcaptcha{xdata = X},
+           BodyString = {?T("Your subscription request and/or messages to ~s have been blocked. "
+                            "To unblock your subscription request, visit ~s"), [JID, get_url(Id)]},
+           Body = xmpp:mk_text(BodyString, Lang),
+           OOB = #oob_x{url = get_url(Id)},
+           Hint = #hint{type = 'no-store'},
+           Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}),
+           ets:insert(captcha,
+                      #captcha{id = Id, pid = self(), key = Key, tref = Tref,
+                               args = Args}),
+           {ok, Id, Body, [Hint, OOB, Captcha, Data]};
+       Err -> Err
     end.
 
 -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) ->
     case create_image(Limiter) of
       {ok, Type, Key, Image} ->
-         Id = <<(p1_rand:get_string())/binary>>,
-         CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>,
-         Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image},
-         HelpTxt = translate:translate(Lang,
-                                       ?T("If you don't see the CAPTCHA image here, "
-                                          "visit the web page.")),
-         Imageurl = get_url(<<Id/binary, "/image">>),
-         NewFs = [mk_field(hidden, <<"FORM_TYPE">>, ?NS_CAPTCHA)|Fs] ++
-               [#xdata_field{type = fixed, var = <<"captcha-fallback-text">>, values = [HelpTxt]},
-                #xdata_field{type = hidden, var = <<"captchahidden">>,
-                             values = [<<"workaround-for-psi">>]},
-                #xdata_field{type = 'text-single', var = <<"captcha-fallback-url">>,
-                             label = translate:translate(
-                                       Lang, ?T("CAPTCHA web page")),
-                             values = [Imageurl]},
-                mk_field(hidden, <<"from">>, jid:encode(To)),
-                mk_field(hidden, <<"challenge">>, Id),
-                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
+           Id = <<(p1_rand:get_string())/binary>>,
+           CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>,
+           Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image},
+           HelpTxt = translate:translate(
+                       Lang, ?T("If you don't see the CAPTCHA image here, visit the web page.")),
+           Imageurl = get_url(<<Id/binary, "/image">>),
+           [H|T] = captcha_form:encode(
+                     [{'captcha-fallback-text', HelpTxt},
+                      {'captcha-fallback-url', Imageurl},
+                      {from, To}, {challenge, Id}, {sid, SID},
+                      mk_ocr_field(Lang, CID, Type)],
+                     Lang, [challenge]),
+           Captcha = X#xdata{type = form, fields = [H|Fs ++ T]},
+           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.
 
 -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}.
 
 process_reply(#xdata{} = X) ->
-    case {xmpp_util:get_xdata_values(<<"challenge">>, X),
-         xmpp_util:get_xdata_values(<<"ocr">>, X)} of
-       {[Id], [OCR]} ->
+    Required = [<<"challenge">>, <<"ocr">>],
+    Fs = lists:filter(
+          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
                captcha_valid -> ok;
                captcha_non_valid -> {error, bad_match};
                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}
     end;
 process_reply(#xcaptcha{xdata = #xdata{} = X}) ->