mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-24 17:29:28 +01:00
Rewrite captcha to use XML generator
This commit is contained in:
parent
96e912b09a
commit
b31ebd2ea0
@ -294,6 +294,12 @@
|
||||
type :: 'none' | 'pending' | 'subscribed' | 'unconfigured'}).
|
||||
-type pubsub_subscription() :: #pubsub_subscription{}.
|
||||
|
||||
-record(bob_data, {cid :: binary(),
|
||||
'max-age' :: non_neg_integer(),
|
||||
type :: binary(),
|
||||
data = <<>> :: any()}).
|
||||
-type bob_data() :: #bob_data{}.
|
||||
|
||||
-record(muc_item, {actor :: #muc_actor{},
|
||||
continue :: binary(),
|
||||
reason = <<>> :: 'undefined' | binary(),
|
||||
@ -404,7 +410,8 @@
|
||||
required = false :: boolean(),
|
||||
desc :: binary(),
|
||||
values = [] :: [binary()],
|
||||
options = [] :: [#xdata_option{}]}).
|
||||
options = [] :: [#xdata_option{}],
|
||||
sub_els = [] :: [any()]}).
|
||||
-type xdata_field() :: #xdata_field{}.
|
||||
|
||||
-record(version, {name :: binary(),
|
||||
@ -482,6 +489,15 @@
|
||||
number :: binary()}).
|
||||
-type vcard_tel() :: #vcard_tel{}.
|
||||
|
||||
-record(media_uri, {type :: binary(),
|
||||
uri = <<>> :: binary()}).
|
||||
-type media_uri() :: #media_uri{}.
|
||||
|
||||
-record(media, {height :: non_neg_integer(),
|
||||
width :: non_neg_integer(),
|
||||
uri = [] :: [#media_uri{}]}).
|
||||
-type media() :: #media{}.
|
||||
|
||||
-record(muc_destroy, {xmlns :: binary(),
|
||||
jid :: any(),
|
||||
reason = <<>> :: 'undefined' | binary(),
|
||||
@ -531,6 +547,11 @@
|
||||
url = [] :: [#bookmark_url{}]}).
|
||||
-type bookmark_storage() :: #bookmark_storage{}.
|
||||
|
||||
-record(oob_x, {url :: binary(),
|
||||
desc = <<>> :: binary(),
|
||||
sid = <<>> :: binary()}).
|
||||
-type oob_x() :: #oob_x{}.
|
||||
|
||||
-record(vcard_sound, {phonetic :: binary(),
|
||||
binval :: any(),
|
||||
extval :: binary()}).
|
||||
@ -582,6 +603,9 @@
|
||||
fields = [] :: [#xdata_field{}]}).
|
||||
-type xdata() :: #xdata{}.
|
||||
|
||||
-record(xcaptcha, {xdata :: #xdata{}}).
|
||||
-type xcaptcha() :: #xcaptcha{}.
|
||||
|
||||
-record(adhoc_command, {node :: binary(),
|
||||
action = execute :: 'cancel' | 'complete' | 'execute' | 'next' | 'prev',
|
||||
sid :: binary(),
|
||||
@ -647,7 +671,8 @@
|
||||
misc :: 'none' | binary(),
|
||||
text :: 'none' | binary(),
|
||||
key :: 'none' | binary(),
|
||||
xdata :: #xdata{}}).
|
||||
xdata :: #xdata{},
|
||||
sub_els = [] :: [any()]}).
|
||||
-type register() :: #register{}.
|
||||
|
||||
-record(disco_info, {node :: binary(),
|
||||
@ -807,6 +832,8 @@
|
||||
version() |
|
||||
pubsub_affiliation() |
|
||||
mam_fin() |
|
||||
bob_data() |
|
||||
media() |
|
||||
sm_a() |
|
||||
carbons_sent() |
|
||||
mam_archived() |
|
||||
@ -880,11 +907,8 @@
|
||||
sasl_failure() |
|
||||
bookmark_storage() |
|
||||
muc_decline() |
|
||||
sasl_auth() |
|
||||
p1_push() |
|
||||
legacy_auth() |
|
||||
search() |
|
||||
pubsub_publish() |
|
||||
unblock() |
|
||||
nick() |
|
||||
p1_ack() |
|
||||
@ -892,6 +916,7 @@
|
||||
mix_join() |
|
||||
xmpp_session() |
|
||||
xdata() |
|
||||
xcaptcha() |
|
||||
iq() |
|
||||
streamhost() |
|
||||
bind() |
|
||||
@ -917,6 +942,7 @@
|
||||
starttls() |
|
||||
mam_prefs() |
|
||||
sasl_mechanisms() |
|
||||
media_uri() |
|
||||
muc_destroy() |
|
||||
vcard_key() |
|
||||
csi() |
|
||||
@ -949,4 +975,8 @@
|
||||
expire() |
|
||||
muc_unsubscribe() |
|
||||
pubsub_unsubscribe() |
|
||||
chatstate().
|
||||
chatstate() |
|
||||
sasl_auth() |
|
||||
p1_push() |
|
||||
oob_x() |
|
||||
pubsub_publish().
|
||||
|
@ -41,31 +41,17 @@
|
||||
-export([create_captcha/6, build_captcha_html/2,
|
||||
check_captcha/2, process_reply/1, process/2,
|
||||
is_feature_available/0, create_captcha_x/5,
|
||||
create_captcha_x/6, opt_type/1]).
|
||||
|
||||
-include("jlib.hrl").
|
||||
opt_type/1]).
|
||||
|
||||
-include("xmpp.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("ejabberd_http.hrl").
|
||||
|
||||
-define(VFIELD(Type, Var, Value),
|
||||
#xmlel{name = <<"field">>,
|
||||
attrs = [{<<"type">>, Type}, {<<"var">>, Var}],
|
||||
children =
|
||||
[#xmlel{name = <<"value">>, attrs = [],
|
||||
children = [Value]}]}).
|
||||
|
||||
-define(CAPTCHA_TEXT(Lang),
|
||||
translate:translate(Lang,
|
||||
<<"Enter the text you see">>)).
|
||||
|
||||
-define(CAPTCHA_LIFETIME, 120000).
|
||||
|
||||
-define(LIMIT_PERIOD, 60*1000*1000).
|
||||
|
||||
-type error() :: efbig | enodata | limit | malformed_image | timeout.
|
||||
-type image_error() :: efbig | enodata | limit | malformed_image | timeout.
|
||||
|
||||
-record(state, {limits = treap:empty() :: treap:treap()}).
|
||||
|
||||
@ -79,188 +65,82 @@ start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [],
|
||||
[]).
|
||||
|
||||
-spec captcha_text(undefined | binary()) -> binary().
|
||||
captcha_text(Lang) ->
|
||||
translate:translate(Lang, <<"Enter the text you see">>).
|
||||
|
||||
-spec mk_ocr_field(binary() | undefined, binary(), binary()) -> xdata_field().
|
||||
mk_ocr_field(Lang, CID, Type) ->
|
||||
URI = #media_uri{type = Type, uri = <<"cid:", CID/binary>>},
|
||||
#xdata_field{var = <<"ocr">>,
|
||||
label = captcha_text(Lang),
|
||||
required = true,
|
||||
sub_els = [#media{uri = [URI]}]}.
|
||||
|
||||
mk_field(Type, Var, Value) ->
|
||||
#xdata_field{type = Type, var = Var, values = [Value]}.
|
||||
|
||||
-spec create_captcha(binary(), jid(), jid(),
|
||||
binary(), any(), any()) -> {error, error()} |
|
||||
{ok, binary(), [xmlel()]}.
|
||||
binary(), any(), any()) -> {error, image_error()} |
|
||||
{ok, binary(), [text()], [xmlel()]}.
|
||||
|
||||
create_captcha(SID, From, To, Lang, Limiter, Args) ->
|
||||
case create_image(Limiter) of
|
||||
{ok, Type, Key, Image} ->
|
||||
Id = <<(randoms:get_string())/binary>>,
|
||||
B64Image = jlib:encode_base64((Image)),
|
||||
JID = jid:to_string(From),
|
||||
CID = <<"sha1+", (p1_sha:sha(Image))/binary,
|
||||
"@bob.xmpp.org">>,
|
||||
Data = #xmlel{name = <<"data">>,
|
||||
attrs =
|
||||
[{<<"xmlns">>, ?NS_BOB}, {<<"cid">>, CID},
|
||||
{<<"max-age">>, <<"0">>}, {<<"type">>, Type}],
|
||||
children = [{xmlcdata, B64Image}]},
|
||||
Captcha = #xmlel{name = <<"captcha">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_CAPTCHA}],
|
||||
children =
|
||||
[#xmlel{name = <<"x">>,
|
||||
attrs =
|
||||
[{<<"xmlns">>, ?NS_XDATA},
|
||||
{<<"type">>, <<"form">>}],
|
||||
children =
|
||||
[?VFIELD(<<"hidden">>,
|
||||
<<"FORM_TYPE">>,
|
||||
{xmlcdata, ?NS_CAPTCHA}),
|
||||
?VFIELD(<<"hidden">>, <<"from">>,
|
||||
{xmlcdata,
|
||||
jid:to_string(To)}),
|
||||
?VFIELD(<<"hidden">>,
|
||||
<<"challenge">>,
|
||||
{xmlcdata, Id}),
|
||||
?VFIELD(<<"hidden">>, <<"sid">>,
|
||||
{xmlcdata, SID}),
|
||||
#xmlel{name = <<"field">>,
|
||||
attrs =
|
||||
[{<<"var">>, <<"ocr">>},
|
||||
{<<"label">>,
|
||||
?CAPTCHA_TEXT(Lang)}],
|
||||
children =
|
||||
[#xmlel{name =
|
||||
<<"required">>,
|
||||
attrs = [],
|
||||
children = []},
|
||||
#xmlel{name =
|
||||
<<"media">>,
|
||||
attrs =
|
||||
[{<<"xmlns">>,
|
||||
?NS_MEDIA}],
|
||||
children =
|
||||
[#xmlel{name
|
||||
=
|
||||
<<"uri">>,
|
||||
attrs
|
||||
=
|
||||
[{<<"type">>,
|
||||
Type}],
|
||||
children
|
||||
=
|
||||
[{xmlcdata,
|
||||
<<"cid:",
|
||||
CID/binary>>}]}]}]}]}]},
|
||||
CID = <<"sha1+", (p1_sha: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:to_string(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},
|
||||
BodyString1 = translate:translate(Lang,
|
||||
<<"Your messages to ~s are being blocked. "
|
||||
"To unblock them, visit ~s">>),
|
||||
BodyString = iolist_to_binary(io_lib:format(BodyString1,
|
||||
[JID, get_url(Id)])),
|
||||
Body = #xmlel{name = <<"body">>, attrs = [],
|
||||
children = [{xmlcdata, BodyString}]},
|
||||
OOB = #xmlel{name = <<"x">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_OOB}],
|
||||
children =
|
||||
[#xmlel{name = <<"url">>, attrs = [],
|
||||
children = [{xmlcdata, get_url(Id)}]}]},
|
||||
Body = xmpp:mk_text(BodyString, Lang),
|
||||
OOB = #oob_x{url = get_url(Id)},
|
||||
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, OOB, Captcha, Data]};
|
||||
{ok, Id, Body, [OOB, Captcha, Data]};
|
||||
Err -> Err
|
||||
end.
|
||||
|
||||
-spec create_captcha_x(binary(), jid(), binary(),
|
||||
any(), [xmlel()]) -> {ok, [xmlel()]} |
|
||||
{error, error()}.
|
||||
-spec create_captcha_x(binary(), jid(), binary(), any(), xdata()) ->
|
||||
{ok, xdata()} | {error, image_error()}.
|
||||
|
||||
create_captcha_x(SID, To, Lang, Limiter, HeadEls) ->
|
||||
create_captcha_x(SID, To, Lang, Limiter, HeadEls, []).
|
||||
|
||||
-spec create_captcha_x(binary(), jid(), binary(),
|
||||
any(), [xmlel()], [xmlel()]) -> {ok, [xmlel()]} |
|
||||
{error, error()}.
|
||||
|
||||
create_captcha_x(SID, To, Lang, Limiter, HeadEls,
|
||||
TailEls) ->
|
||||
create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) ->
|
||||
case create_image(Limiter) of
|
||||
{ok, Type, Key, Image} ->
|
||||
Id = <<(randoms:get_string())/binary>>,
|
||||
B64Image = jlib:encode_base64((Image)),
|
||||
CID = <<"sha1+", (p1_sha:sha(Image))/binary,
|
||||
"@bob.xmpp.org">>,
|
||||
Data = #xmlel{name = <<"data">>,
|
||||
attrs =
|
||||
[{<<"xmlns">>, ?NS_BOB}, {<<"cid">>, CID},
|
||||
{<<"max-age">>, <<"0">>}, {<<"type">>, Type}],
|
||||
children = [{xmlcdata, B64Image}]},
|
||||
CID = <<"sha1+", (p1_sha:sha(Image))/binary, "@bob.xmpp.org">>,
|
||||
Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image},
|
||||
HelpTxt = translate:translate(Lang,
|
||||
<<"If you don't see the CAPTCHA image here, "
|
||||
"visit the web page.">>),
|
||||
Imageurl = get_url(<<Id/binary, "/image">>),
|
||||
Captcha = #xmlel{name = <<"x">>,
|
||||
attrs =
|
||||
[{<<"xmlns">>, ?NS_XDATA},
|
||||
{<<"type">>, <<"form">>}],
|
||||
children =
|
||||
[?VFIELD(<<"hidden">>, <<"FORM_TYPE">>,
|
||||
{xmlcdata, ?NS_CAPTCHA})
|
||||
| HeadEls]
|
||||
++
|
||||
[#xmlel{name = <<"field">>,
|
||||
attrs = [{<<"type">>, <<"fixed">>}],
|
||||
children =
|
||||
[#xmlel{name = <<"value">>,
|
||||
attrs = [],
|
||||
children =
|
||||
[{xmlcdata,
|
||||
HelpTxt}]}]},
|
||||
#xmlel{name = <<"field">>,
|
||||
attrs =
|
||||
[{<<"type">>, <<"hidden">>},
|
||||
{<<"var">>, <<"captchahidden">>}],
|
||||
children =
|
||||
[#xmlel{name = <<"value">>,
|
||||
attrs = [],
|
||||
children =
|
||||
[{xmlcdata,
|
||||
<<"workaround-for-psi">>}]}]},
|
||||
#xmlel{name = <<"field">>,
|
||||
attrs =
|
||||
[{<<"type">>, <<"text-single">>},
|
||||
{<<"label">>,
|
||||
translate:translate(Lang,
|
||||
<<"CAPTCHA web page">>)},
|
||||
{<<"var">>, <<"url">>}],
|
||||
children =
|
||||
[#xmlel{name = <<"value">>,
|
||||
attrs = [],
|
||||
children =
|
||||
[{xmlcdata,
|
||||
Imageurl}]}]},
|
||||
?VFIELD(<<"hidden">>, <<"from">>,
|
||||
{xmlcdata, jid:to_string(To)}),
|
||||
?VFIELD(<<"hidden">>, <<"challenge">>,
|
||||
{xmlcdata, Id}),
|
||||
?VFIELD(<<"hidden">>, <<"sid">>,
|
||||
{xmlcdata, SID}),
|
||||
#xmlel{name = <<"field">>,
|
||||
attrs =
|
||||
[{<<"var">>, <<"ocr">>},
|
||||
{<<"label">>,
|
||||
?CAPTCHA_TEXT(Lang)}],
|
||||
children =
|
||||
[#xmlel{name = <<"required">>,
|
||||
attrs = [], children = []},
|
||||
#xmlel{name = <<"media">>,
|
||||
attrs =
|
||||
[{<<"xmlns">>,
|
||||
?NS_MEDIA}],
|
||||
children =
|
||||
[#xmlel{name =
|
||||
<<"uri">>,
|
||||
attrs =
|
||||
[{<<"type">>,
|
||||
Type}],
|
||||
children =
|
||||
[{xmlcdata,
|
||||
<<"cid:",
|
||||
CID/binary>>}]}]}]}]
|
||||
++ TailEls},
|
||||
NewFs = [mk_field(hidden, <<"FORM_TYPE">>, ?NS_CAPTCHA)|Fs] ++
|
||||
[#xdata_field{type = fixed, values = [HelpTxt]},
|
||||
#xdata_field{type = hidden, var = <<"captchahidden">>,
|
||||
values = [<<"workaround-for-psi">>]},
|
||||
#xdata_field{type = 'text-single', var = <<"url">>,
|
||||
label = translate:translate(
|
||||
Lang, <<"CAPTCHA web page">>),
|
||||
values = [Imageurl]},
|
||||
mk_field(hidden, <<"from">>, jid:to_string(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,
|
||||
@ -281,7 +161,7 @@ build_captcha_html(Id, Lang) ->
|
||||
attrs =
|
||||
[{<<"src">>, get_url(<<Id/binary, "/image">>)}],
|
||||
children = []},
|
||||
TextEl = {xmlcdata, ?CAPTCHA_TEXT(Lang)},
|
||||
TextEl = {xmlcdata, captcha_text(Lang)},
|
||||
IdEl = #xmlel{name = <<"input">>,
|
||||
attrs =
|
||||
[{<<"type">>, <<"hidden">>}, {<<"name">>, <<"id">>},
|
||||
@ -317,27 +197,24 @@ build_captcha_html(Id, Lang) ->
|
||||
_ -> captcha_not_found
|
||||
end.
|
||||
|
||||
-spec process_reply(xmlel()) -> ok | {error, bad_match | not_found | malformed}.
|
||||
-spec process_reply(xmpp_element()) -> ok | {error, bad_match | not_found | malformed}.
|
||||
|
||||
process_reply(#xmlel{} = El) ->
|
||||
case fxml:get_subtag(El, <<"x">>) of
|
||||
false -> {error, malformed};
|
||||
Xdata ->
|
||||
Fields = jlib:parse_xdata_submit(Xdata),
|
||||
case catch {proplists:get_value(<<"challenge">>,
|
||||
Fields),
|
||||
proplists:get_value(<<"ocr">>, Fields)}
|
||||
of
|
||||
{[Id | _], [OCR | _]} ->
|
||||
case check_captcha(Id, OCR) of
|
||||
captcha_valid -> ok;
|
||||
captcha_non_valid -> {error, bad_match};
|
||||
captcha_not_found -> {error, not_found}
|
||||
end;
|
||||
_ -> {error, malformed}
|
||||
end
|
||||
process_reply(#xdata{} = X) ->
|
||||
case {xmpp_util:get_xdata_values(<<"challenge">>, X),
|
||||
xmpp_util:get_xdata_values(<<"ocr">>, X)} of
|
||||
{[Id], [OCR]} ->
|
||||
case check_captcha(Id, OCR) of
|
||||
captcha_valid -> ok;
|
||||
captcha_non_valid -> {error, bad_match};
|
||||
captcha_not_found -> {error, not_found}
|
||||
end;
|
||||
_ ->
|
||||
{error, malformed}
|
||||
end;
|
||||
process_reply(_) -> {error, malformed}.
|
||||
process_reply(#xcaptcha{xdata = #xdata{} = X}) ->
|
||||
process_reply(X);
|
||||
process_reply(_) ->
|
||||
{error, malformed}.
|
||||
|
||||
process(_Handlers,
|
||||
#request{method = 'GET', lang = Lang,
|
||||
|
@ -1824,8 +1824,9 @@ add_new_user(From, Nick, Packet, StateData) ->
|
||||
case ejabberd_captcha:create_captcha(SID, RoomJID, To,
|
||||
Lang, Limiter, From)
|
||||
of
|
||||
{ok, ID, CaptchaEls} ->
|
||||
MsgPkt = #message{id = ID, sub_els = CaptchaEls},
|
||||
{ok, ID, Body, CaptchaEls} ->
|
||||
MsgPkt = #message{id = ID, body = Body,
|
||||
sub_els = CaptchaEls},
|
||||
Robots = (?DICT):store(From, {Nick, Packet},
|
||||
StateData#state.robots),
|
||||
ejabberd_router:route(RoomJID, From, MsgPkt),
|
||||
|
@ -218,10 +218,10 @@ process_iq(#iq{type = get, from = From, to = To, id = ID, lang = Lang} = IQ,
|
||||
X = #xdata{type = form, instructions = [Instr],
|
||||
fields = [UField, PField]},
|
||||
case ejabberd_captcha:create_captcha_x(ID, To, Lang, Source, X) of
|
||||
{ok, Captcha} ->
|
||||
{ok, CaptchaEls} ->
|
||||
xmpp:make_iq_result(
|
||||
IQ, #register{instructions = TopInstr,
|
||||
xdata = Captcha});
|
||||
sub_els = CaptchaEls});
|
||||
{error, limit} ->
|
||||
ErrText = <<"Too many CAPTCHA requests">>,
|
||||
xmpp:make_error(
|
||||
|
@ -60,7 +60,7 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("jlib.hrl").
|
||||
-include("xmpp.hrl").
|
||||
|
||||
-include("ejabberd_http.hrl").
|
||||
|
||||
@ -334,7 +334,7 @@ build_captcha_li_list2(Lang, IP) ->
|
||||
case ejabberd_captcha:create_captcha(SID, From, To,
|
||||
Lang, IP, Args)
|
||||
of
|
||||
{ok, Id, _} ->
|
||||
{ok, Id, _, _} ->
|
||||
{_, {CImg, CText, CId, CKey}} =
|
||||
ejabberd_captcha:build_captcha_html(Id, Lang),
|
||||
[?XE(<<"li">>,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1010,7 +1010,7 @@
|
||||
'$username', '$nick', '$password', '$name',
|
||||
'$first', '$last', '$email', '$address',
|
||||
'$city', '$state', '$zip', '$phone', '$url',
|
||||
'$date', '$misc', '$text', '$key', '$xdata'},
|
||||
'$date', '$misc', '$text', '$key', '$xdata', '$_els'},
|
||||
refs = [#ref{name = xdata, min = 0, max = 1,
|
||||
label = '$xdata'},
|
||||
#ref{name = register_registered, min = 0, max = 1,
|
||||
@ -1600,7 +1600,7 @@
|
||||
#elem{name = <<"field">>,
|
||||
xmlns = <<"jabber:x:data">>,
|
||||
result = {xdata_field, '$label', '$type', '$var',
|
||||
'$required', '$desc', '$values', '$options'},
|
||||
'$required', '$desc', '$values', '$options', '$_els'},
|
||||
attrs = [#attr{name = <<"label">>},
|
||||
#attr{name = <<"type">>,
|
||||
enc = {enc_enum, []},
|
||||
@ -2937,6 +2937,66 @@
|
||||
#attr{name = <<"version">>, default = <<"">>},
|
||||
#attr{name = <<"id">>, default = <<"">>}]}).
|
||||
|
||||
-xml(bob_data,
|
||||
#elem{name = <<"data">>,
|
||||
xmlns = <<"urn:xmpp:bob">>,
|
||||
result = {bob_data, '$cid', '$max-age', '$type', '$data'},
|
||||
attrs = [#attr{name = <<"cid">>, required = true},
|
||||
#attr{name = <<"max-age">>,
|
||||
dec = {dec_int, [0, infinity]},
|
||||
enc = {enc_int, []}},
|
||||
#attr{name = <<"type">>}],
|
||||
cdata = #cdata{label = '$data', default = <<"">>,
|
||||
dec = {base64, decode, []},
|
||||
enc = {base64, encode, []}}}).
|
||||
|
||||
-xml(captcha,
|
||||
#elem{name = <<"captcha">>,
|
||||
xmlns = <<"urn:xmpp:captcha">>,
|
||||
result = {xcaptcha, '$xdata'},
|
||||
refs = [#ref{name = xdata, min = 1, max = 1}]}).
|
||||
|
||||
-xml(media_uri,
|
||||
#elem{name = <<"uri">>,
|
||||
xmlns = <<"urn:xmpp:media-element">>,
|
||||
result = {media_uri, '$type', '$uri'},
|
||||
attrs = [#attr{name = <<"type">>, required = true}],
|
||||
cdata = #cdata{label = '$uri', default = <<"">>}}).
|
||||
|
||||
-xml(media,
|
||||
#elem{name = <<"media">>,
|
||||
xmlns = <<"urn:xmpp:media-element">>,
|
||||
result = {media, '$height', '$width', '$uri'},
|
||||
attrs = [#attr{name = <<"height">>,
|
||||
dec = {dec_int, [0, infinity]},
|
||||
enc = {enc_int, []}},
|
||||
#attr{name = <<"width">>,
|
||||
dec = {dec_int, [0, inifinity]},
|
||||
enc = {enc_int, []}}],
|
||||
refs = [#ref{name = media_uri, label = '$uri'}]}).
|
||||
|
||||
-xml(oob_url,
|
||||
#elem{name = <<"url">>,
|
||||
xmlns = <<"jabber:x:oob">>,
|
||||
result = '$cdata',
|
||||
cdata = #cdata{required = true}}).
|
||||
|
||||
-xml(oob_desc,
|
||||
#elem{name = <<"desc">>,
|
||||
xmlns = <<"jabber:x:oob">>,
|
||||
result = '$cdata',
|
||||
cdata = #cdata{default = <<"">>}}).
|
||||
|
||||
-xml(oob_x,
|
||||
#elem{name = <<"x">>,
|
||||
xmlns = <<"jabber:x:oob">>,
|
||||
result = {oob_x, '$url', '$desc', '$sid'},
|
||||
attrs = [#attr{name = <<"sid">>, default = <<"">>}],
|
||||
refs = [#ref{name = oob_url, min = 1, max = 1,
|
||||
label = '$url'},
|
||||
#ref{name = oob_desc, default = <<"">>,
|
||||
min = 0, max = 1, label = '$desc'}]}).
|
||||
|
||||
dec_tzo(Val) ->
|
||||
[H1, M1] = str:tokens(Val, <<":">>),
|
||||
H = jlib:binary_to_integer(H1),
|
||||
|
Loading…
Reference in New Issue
Block a user