diff --git a/include/ns.hrl b/include/ns.hrl index b30161565..7955129ef 100644 --- a/include/ns.hrl +++ b/include/ns.hrl @@ -161,6 +161,8 @@ -define(NS_HTTP_UPLOAD_OLD, <<"eu:siacs:conversations:http:upload">>). -define(NS_THUMBS_1, <<"urn:xmpp:thumbs:1">>). -define(NS_NICK, <<"http://jabber.org/protocol/nick">>). +-define(NS_SIC_0, <<"urn:xmpp:sic:0">>). +-define(NS_SIC_1, <<"urn:xmpp:sic:1">>). -define(NS_MIX_0, <<"urn:xmpp:mix:0">>). -define(NS_MIX_SERVICEINFO_0, <<"urn:xmpp:mix:0#serviceinfo">>). -define(NS_MIX_NODES_MESSAGES, <<"urn:xmpp:mix:nodes:messages">>). diff --git a/include/xmpp_codec.hrl b/include/xmpp_codec.hrl index 1ede0ff1d..88c94a76b 100644 --- a/include/xmpp_codec.hrl +++ b/include/xmpp_codec.hrl @@ -343,6 +343,11 @@ items = [] :: [#pubsub_item{}]}). -type pubsub_items() :: #pubsub_items{}. +-record(sic, {ip :: any(), + port :: non_neg_integer(), + xmlns :: binary()}). +-type sic() :: #sic{}. + -record(carbons_sent, {forwarded :: #forwarded{}}). -type carbons_sent() :: #carbons_sent{}. @@ -909,7 +914,6 @@ muc_decline() | legacy_auth() | search() | - unblock() | nick() | p1_ack() | block() | @@ -939,6 +943,7 @@ stream_features() | stats() | pubsub_items() | + sic() | starttls() | mam_prefs() | sasl_mechanisms() | @@ -979,4 +984,5 @@ sasl_auth() | p1_push() | oob_x() | - pubsub_publish(). + pubsub_publish() | + unblock(). diff --git a/src/mod_sic.erl b/src/mod_sic.erl index 49b65a0ee..4bb4eb9eb 100644 --- a/src/mod_sic.erl +++ b/src/mod_sic.erl @@ -31,73 +31,66 @@ -behaviour(gen_mod). --export([start/2, stop/1, process_local_iq/3, - process_sm_iq/3, mod_opt_type/1, depends/2]). +-export([start/2, stop/1, process_local_iq/1, + process_sm_iq/1, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). - --include("jlib.hrl"). - --define(NS_SIC, <<"urn:xmpp:sic:0">>). +-include("xmpp.hrl"). start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1, one_queue), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_SIC, ?MODULE, process_local_iq, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_SIC, ?MODULE, process_sm_iq, IQDisc). + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_SIC_0, + ?MODULE, process_local_iq, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_SIC_0, + ?MODULE, process_sm_iq, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_SIC_1, + ?MODULE, process_local_iq, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_SIC_1, + ?MODULE, process_sm_iq, IQDisc). stop(Host) -> - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, - ?NS_SIC), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, - ?NS_SIC). + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_SIC_0), + gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_SIC_0), + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_SIC_1), + gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_SIC_1). depends(_Host, _Opts) -> []. -process_local_iq(#jid{user = User, server = Server, - resource = Resource}, - _To, #iq{type = get, sub_el = _SubEl} = IQ) -> +process_local_iq(#iq{from = #jid{user = User, server = Server, + resource = Resource}, + type = get} = IQ) -> get_ip({User, Server, Resource}, IQ); -process_local_iq(_From, _To, - #iq{type = set, sub_el = SubEl, lang = Lang} = IQ) -> +process_local_iq(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, - IQ#iq{type = error, sub_el = [SubEl, ?ERRT_NOT_ALLOWED(Lang, Txt)]}. + xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)). -process_sm_iq(#jid{user = User, server = Server, - resource = Resource}, - #jid{user = User, server = Server}, - #iq{type = get, sub_el = _SubEl} = IQ) -> +process_sm_iq(#iq{from = #jid{user = User, server = Server, + resource = Resource}, + to = #jid{user = User, server = Server}, + type = get} = IQ) -> get_ip({User, Server, Resource}, IQ); -process_sm_iq(_From, _To, - #iq{type = get, sub_el = SubEl, lang = Lang} = IQ) -> +process_sm_iq(#iq{type = get, lang = Lang} = IQ) -> Txt = <<"Query to another users is forbidden">>, - IQ#iq{type = error, sub_el = [SubEl, ?ERRT_FORBIDDEN(Lang, Txt)]}; -process_sm_iq(_From, _To, - #iq{type = set, sub_el = SubEl, lang = Lang} = IQ) -> + xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)); +process_sm_iq(#iq{type = set, lang = Lang} = IQ) -> Txt = <<"Value 'set' of 'type' attribute is not allowed">>, - IQ#iq{type = error, sub_el = [SubEl, ?ERRT_NOT_ALLOWED(Lang, Txt)]}. + xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)). get_ip({User, Server, Resource}, - #iq{lang = Lang, - sub_el = - #xmlel{name = Name, attrs = Attrs} = SubEl} = - IQ) -> + #iq{lang = Lang, sub_els = [#sic{xmlns = NS}]} = IQ) -> case ejabberd_sm:get_user_ip(User, Server, Resource) of - {IP, _} when is_tuple(IP) -> - IQ#iq{type = result, - sub_el = - [#xmlel{name = Name, attrs = Attrs, - children = - [{xmlcdata, - iolist_to_binary(jlib:ip_to_list(IP))}]}]}; - _ -> - Txt = <<"User session not found">>, - IQ#iq{type = error, - sub_el = [SubEl, ?ERRT_INTERNAL_SERVER_ERROR(Lang, Txt)]} + {IP, Port} when is_tuple(IP) -> + Result = case NS of + ?NS_SIC_0 -> #sic{ip = IP, xmlns = NS}; + ?NS_SIC_1 -> #sic{ip = IP, port = Port, xmlns = NS} + end, + xmpp:make_iq_result(IQ, Result); + _ -> + Txt = <<"User session not found">>, + xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang)) end. mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; diff --git a/src/xmpp_codec.erl b/src/xmpp_codec.erl index 113be860b..11bd741f4 100644 --- a/src/xmpp_codec.erl +++ b/src/xmpp_codec.erl @@ -15,6 +15,16 @@ decode(_el) -> decode(_el, []). decode({xmlel, _name, _attrs, _} = _el, Opts) -> IgnoreEls = proplists:get_bool(ignore_els, Opts), case {_name, get_attr(<<"xmlns">>, _attrs)} of + {<<"address">>, <<"urn:xmpp:sic:0">>} -> + decode_sic(<<"urn:xmpp:sic:0">>, IgnoreEls, _el); + {<<"address">>, <<"urn:xmpp:sic:1">>} -> + decode_sic(<<"urn:xmpp:sic:1">>, IgnoreEls, _el); + {<<"port">>, <<"urn:xmpp:sic:1">>} -> + decode_sip_port(<<"urn:xmpp:sic:1">>, IgnoreEls, _el); + {<<"ip">>, <<"urn:xmpp:sic:0">>} -> + decode_sic_ip(<<"urn:xmpp:sic:0">>, IgnoreEls, _el); + {<<"ip">>, <<"urn:xmpp:sic:1">>} -> + decode_sic_ip(<<"urn:xmpp:sic:1">>, IgnoreEls, _el); {<<"x">>, <<"jabber:x:oob">>} -> decode_oob_x(<<"jabber:x:oob">>, IgnoreEls, _el); {<<"desc">>, <<"jabber:x:oob">>} -> @@ -1309,6 +1319,11 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) -> is_known_tag({xmlel, _name, _attrs, _} = _el) -> case {_name, get_attr(<<"xmlns">>, _attrs)} of + {<<"address">>, <<"urn:xmpp:sic:0">>} -> true; + {<<"address">>, <<"urn:xmpp:sic:1">>} -> true; + {<<"port">>, <<"urn:xmpp:sic:1">>} -> true; + {<<"ip">>, <<"urn:xmpp:sic:0">>} -> true; + {<<"ip">>, <<"urn:xmpp:sic:1">>} -> true; {<<"x">>, <<"jabber:x:oob">>} -> true; {<<"desc">>, <<"jabber:x:oob">>} -> true; {<<"url">>, <<"jabber:x:oob">>} -> true; @@ -2609,7 +2624,9 @@ encode({media, _, _, _} = Media) -> encode_media(Media, [{<<"xmlns">>, <<"urn:xmpp:media-element">>}]); encode({oob_x, _, _, _} = X) -> - encode_oob_x(X, [{<<"xmlns">>, <<"jabber:x:oob">>}]). + encode_oob_x(X, [{<<"xmlns">>, <<"jabber:x:oob">>}]); +encode({sic, _, _, _} = Address) -> + encode_sic(Address, []). get_name({last, _, _}) -> <<"query">>; get_name({version, _, _, _}) -> <<"query">>; @@ -2802,7 +2819,8 @@ get_name({bob_data, _, _, _, _}) -> <<"data">>; get_name({xcaptcha, _}) -> <<"captcha">>; get_name({media_uri, _, _}) -> <<"uri">>; get_name({media, _, _, _}) -> <<"media">>; -get_name({oob_x, _, _, _}) -> <<"x">>. +get_name({oob_x, _, _, _}) -> <<"x">>; +get_name({sic, _, _, _}) -> <<"address">>. get_ns({last, _, _}) -> <<"jabber:iq:last">>; get_ns({version, _, _, _}) -> <<"jabber:iq:version">>; @@ -3070,7 +3088,8 @@ get_ns({media_uri, _, _}) -> <<"urn:xmpp:media-element">>; get_ns({media, _, _, _}) -> <<"urn:xmpp:media-element">>; -get_ns({oob_x, _, _, _}) -> <<"jabber:x:oob">>. +get_ns({oob_x, _, _, _}) -> <<"jabber:x:oob">>; +get_ns({sic, _, _, Xmlns}) -> Xmlns. dec_int(Val) -> dec_int(Val, infinity, infinity). @@ -3319,8 +3338,18 @@ pp(xcaptcha, 1) -> [xdata]; pp(media_uri, 2) -> [type, uri]; pp(media, 3) -> [height, width, uri]; pp(oob_x, 3) -> [url, desc, sid]; +pp(sic, 3) -> [ip, port, xmlns]; pp(_, _) -> no. +enc_ip({0, 0, 0, 0, 0, 65535, A, B}) -> + enc_ip({(A bsr 8) band 255, A band 255, + (B bsr 8) band 255, B band 255}); +enc_ip(Addr) -> list_to_binary(inet_parse:ntoa(Addr)). + +dec_ip(S) -> + {ok, Addr} = inet_parse:address(binary_to_list(S)), + Addr. + join([], _Sep) -> <<>>; join([H | T], Sep) -> <> || X <- T >>/binary>>. @@ -3365,6 +3394,155 @@ dec_tzo(Val) -> M = jlib:binary_to_integer(M1), if H >= -12, H =< 12, M >= 0, M < 60 -> {H, M} end. +decode_sic(__TopXMLNS, __IgnoreEls, + {xmlel, <<"address">>, _attrs, _els}) -> + {Ip, Port} = decode_sic_els(__TopXMLNS, __IgnoreEls, + _els, undefined, undefined), + Xmlns = decode_sic_attrs(__TopXMLNS, _attrs, undefined), + {sic, Ip, Port, Xmlns}. + +decode_sic_els(__TopXMLNS, __IgnoreEls, [], Ip, Port) -> + {Ip, Port}; +decode_sic_els(__TopXMLNS, __IgnoreEls, + [{xmlel, <<"ip">>, _attrs, _} = _el | _els], Ip, + Port) -> + case get_attr(<<"xmlns">>, _attrs) of + <<"">> + when __TopXMLNS == <<"urn:xmpp:sic:1">>; + __TopXMLNS == <<"urn:xmpp:sic:0">> -> + decode_sic_els(__TopXMLNS, __IgnoreEls, _els, + decode_sic_ip(__TopXMLNS, __IgnoreEls, _el), Port); + <<"urn:xmpp:sic:0">> -> + decode_sic_els(__TopXMLNS, __IgnoreEls, _els, + decode_sic_ip(<<"urn:xmpp:sic:0">>, __IgnoreEls, _el), + Port); + <<"urn:xmpp:sic:1">> -> + decode_sic_els(__TopXMLNS, __IgnoreEls, _els, + decode_sic_ip(<<"urn:xmpp:sic:1">>, __IgnoreEls, _el), + Port); + _ -> + decode_sic_els(__TopXMLNS, __IgnoreEls, _els, Ip, Port) + end; +decode_sic_els(__TopXMLNS, __IgnoreEls, + [{xmlel, <<"port">>, _attrs, _} = _el | _els], Ip, + Port) -> + case get_attr(<<"xmlns">>, _attrs) of + <<"">> when __TopXMLNS == <<"urn:xmpp:sic:1">> -> + decode_sic_els(__TopXMLNS, __IgnoreEls, _els, Ip, + decode_sip_port(__TopXMLNS, __IgnoreEls, _el)); + <<"urn:xmpp:sic:1">> -> + decode_sic_els(__TopXMLNS, __IgnoreEls, _els, Ip, + decode_sip_port(<<"urn:xmpp:sic:1">>, __IgnoreEls, + _el)); + _ -> + decode_sic_els(__TopXMLNS, __IgnoreEls, _els, Ip, Port) + end; +decode_sic_els(__TopXMLNS, __IgnoreEls, [_ | _els], Ip, + Port) -> + decode_sic_els(__TopXMLNS, __IgnoreEls, _els, Ip, Port). + +decode_sic_attrs(__TopXMLNS, + [{<<"xmlns">>, _val} | _attrs], _Xmlns) -> + decode_sic_attrs(__TopXMLNS, _attrs, _val); +decode_sic_attrs(__TopXMLNS, [_ | _attrs], Xmlns) -> + decode_sic_attrs(__TopXMLNS, _attrs, Xmlns); +decode_sic_attrs(__TopXMLNS, [], Xmlns) -> + decode_sic_attr_xmlns(__TopXMLNS, Xmlns). + +encode_sic({sic, Ip, Port, Xmlns}, _xmlns_attrs) -> + _els = lists:reverse('encode_sic_$ip'(Ip, + 'encode_sic_$port'(Port, []))), + _attrs = encode_sic_attr_xmlns(Xmlns, _xmlns_attrs), + {xmlel, <<"address">>, _attrs, _els}. + +'encode_sic_$ip'(undefined, _acc) -> _acc; +'encode_sic_$ip'(Ip, _acc) -> + [encode_sic_ip(Ip, []) | _acc]. + +'encode_sic_$port'(undefined, _acc) -> _acc; +'encode_sic_$port'(Port, _acc) -> + [encode_sip_port(Port, []) | _acc]. + +decode_sic_attr_xmlns(__TopXMLNS, undefined) -> + undefined; +decode_sic_attr_xmlns(__TopXMLNS, _val) -> _val. + +encode_sic_attr_xmlns(undefined, _acc) -> _acc; +encode_sic_attr_xmlns(_val, _acc) -> + [{<<"xmlns">>, _val} | _acc]. + +decode_sip_port(__TopXMLNS, __IgnoreEls, + {xmlel, <<"port">>, _attrs, _els}) -> + Cdata = decode_sip_port_els(__TopXMLNS, __IgnoreEls, + _els, <<>>), + Cdata. + +decode_sip_port_els(__TopXMLNS, __IgnoreEls, [], + Cdata) -> + decode_sip_port_cdata(__TopXMLNS, Cdata); +decode_sip_port_els(__TopXMLNS, __IgnoreEls, + [{xmlcdata, _data} | _els], Cdata) -> + decode_sip_port_els(__TopXMLNS, __IgnoreEls, _els, + <>); +decode_sip_port_els(__TopXMLNS, __IgnoreEls, [_ | _els], + Cdata) -> + decode_sip_port_els(__TopXMLNS, __IgnoreEls, _els, + Cdata). + +encode_sip_port(Cdata, _xmlns_attrs) -> + _els = encode_sip_port_cdata(Cdata, []), + _attrs = _xmlns_attrs, + {xmlel, <<"port">>, _attrs, _els}. + +decode_sip_port_cdata(__TopXMLNS, <<>>) -> + erlang:error({xmpp_codec, + {missing_cdata, <<>>, <<"port">>, __TopXMLNS}}); +decode_sip_port_cdata(__TopXMLNS, _val) -> + case catch dec_int(_val, 0, 65535) of + {'EXIT', _} -> + erlang:error({xmpp_codec, + {bad_cdata_value, <<>>, <<"port">>, __TopXMLNS}}); + _res -> _res + end. + +encode_sip_port_cdata(_val, _acc) -> + [{xmlcdata, enc_int(_val)} | _acc]. + +decode_sic_ip(__TopXMLNS, __IgnoreEls, + {xmlel, <<"ip">>, _attrs, _els}) -> + Cdata = decode_sic_ip_els(__TopXMLNS, __IgnoreEls, _els, + <<>>), + Cdata. + +decode_sic_ip_els(__TopXMLNS, __IgnoreEls, [], Cdata) -> + decode_sic_ip_cdata(__TopXMLNS, Cdata); +decode_sic_ip_els(__TopXMLNS, __IgnoreEls, + [{xmlcdata, _data} | _els], Cdata) -> + decode_sic_ip_els(__TopXMLNS, __IgnoreEls, _els, + <>); +decode_sic_ip_els(__TopXMLNS, __IgnoreEls, [_ | _els], + Cdata) -> + decode_sic_ip_els(__TopXMLNS, __IgnoreEls, _els, Cdata). + +encode_sic_ip(Cdata, _xmlns_attrs) -> + _els = encode_sic_ip_cdata(Cdata, []), + _attrs = _xmlns_attrs, + {xmlel, <<"ip">>, _attrs, _els}. + +decode_sic_ip_cdata(__TopXMLNS, <<>>) -> + erlang:error({xmpp_codec, + {missing_cdata, <<>>, <<"ip">>, __TopXMLNS}}); +decode_sic_ip_cdata(__TopXMLNS, _val) -> + case catch dec_ip(_val) of + {'EXIT', _} -> + erlang:error({xmpp_codec, + {bad_cdata_value, <<>>, <<"ip">>, __TopXMLNS}}); + _res -> _res + end. + +encode_sic_ip_cdata(_val, _acc) -> + [{xmlcdata, enc_ip(_val)} | _acc]. + decode_oob_x(__TopXMLNS, __IgnoreEls, {xmlel, <<"x">>, _attrs, _els}) -> {Desc, Url} = decode_oob_x_els(__TopXMLNS, __IgnoreEls, diff --git a/tools/xmpp_codec.spec b/tools/xmpp_codec.spec index f72e250f2..9cb14282c 100644 --- a/tools/xmpp_codec.spec +++ b/tools/xmpp_codec.spec @@ -2997,6 +2997,30 @@ #ref{name = oob_desc, default = <<"">>, min = 0, max = 1, label = '$desc'}]}). +-xml(sic_ip, + #elem{name = <<"ip">>, + xmlns = [<<"urn:xmpp:sic:0">>, <<"urn:xmpp:sic:1">>], + result = '$cdata', + cdata = #cdata{required = true, + dec = {dec_ip, []}, + enc = {enc_ip, []}}}). + +-xml(sip_port, + #elem{name = <<"port">>, + xmlns = <<"urn:xmpp:sic:1">>, + result = '$cdata', + cdata = #cdata{required = true, + dec = {dec_int, [0, 65535]}, + enc = {enc_int, []}}}). + +-xml(sic, + #elem{name = <<"address">>, + xmlns = [<<"urn:xmpp:sic:0">>, <<"urn:xmpp:sic:1">>], + result = {sic, '$ip', '$port', '$xmlns'}, + attrs = [#attr{name = <<"xmlns">>}], + refs = [#ref{name = sic_ip, min = 0, max = 1, label = '$ip'}, + #ref{name = sip_port, min = 0, max = 1, label = '$port'}]}). + dec_tzo(Val) -> [H1, M1] = str:tokens(Val, <<":">>), H = jlib:binary_to_integer(H1), @@ -3050,6 +3074,16 @@ join([], _Sep) -> <<>>; join([H | T], Sep) -> <> || X <- T >>)/binary>>. +dec_ip(S) -> + {ok, Addr} = inet_parse:address(binary_to_list(S)), + Addr. + +enc_ip({0,0,0,0,0,16#ffff,A,B}) -> + enc_ip({(A bsr 8) band 16#ff, A band 16#ff, + (B bsr 8) band 16#ff, B band 16#ff}); +enc_ip(Addr) -> + list_to_binary(inet_parse:ntoa(Addr)). + %% Local Variables: %% mode: erlang %% End: