Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/processone/ejabberd.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2010-05-10 10:00:30 +0400
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2010-05-10 10:00:30 +0400
commit92ec42565e94be7ec23c65164716438d5fe14b34 (patch)
treef3940733c1964d3a06b0dbcd2cfcadaf7c7f9efc
parentf84a1c88cfafef82796b380fb20e5bd6022c4454 (diff)
full support for XEP-0115 v1.5 (EJAB-1223) (EJAB-1189)
-rw-r--r--src/ejabberd_c2s.erl31
-rw-r--r--src/ejabberd_s2s_in.erl19
-rw-r--r--src/mod_caps.erl161
-rw-r--r--src/mod_disco.erl43
-rw-r--r--src/mod_register.erl4
5 files changed, 222 insertions, 36 deletions
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 6474e239c..1bb7dd950 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -323,9 +323,9 @@ wait_for_stream({xmlstreamstart, #xmlel{ns = NS} = Opening}, StateData) ->
[]
end,
Other_Feats = ejabberd_hooks:run_fold(
- c2s_stream_features,
- ServerB,
- [], []),
+ c2s_stream_features,
+ ServerB,
+ [], [ServerB]),
send_element(StateData,
exmpp_stream:features(
TLSFeature ++
@@ -340,17 +340,26 @@ wait_for_stream({xmlstreamstart, #xmlel{ns = NS} = Opening}, StateData) ->
_ ->
case StateData#state.resource of
undefined ->
- RosterVersioningFeature = ejabberd_hooks:run_fold(roster_get_versioning_feature, ServerB, [], [ServerB]),
+ RosterVersioningFeature =
+ ejabberd_hooks:run_fold(
+ roster_get_versioning_feature,
+ ServerB,
+ [], [ServerB]),
+ Other_Feats = ejabberd_hooks:run_fold(
+ c2s_stream_features,
+ ServerB,
+ [], [ServerB]),
send_element(
StateData,
- exmpp_stream:features([
- exmpp_server_binding:feature(),
- exmpp_server_session:feature()
- | RosterVersioningFeature])),
+ exmpp_stream:features(
+ [exmpp_server_binding:feature(),
+ exmpp_server_session:feature()]
+ ++ RosterVersioningFeature
+ ++ Other_Feats)),
fsm_next_state(wait_for_bind,
- StateData#state{
- server = ServerB,
- lang = Lang});
+ StateData#state{
+ server = ServerB,
+ lang = Lang});
_ ->
send_element(
StateData,
diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl
index 73f40ee5f..8a881b145 100644
--- a/src/ejabberd_s2s_in.erl
+++ b/src/ejabberd_s2s_in.erl
@@ -155,8 +155,9 @@ init([{SockMod, Socket}, Opts]) ->
wait_for_stream({xmlstreamstart, Opening}, StateData) ->
case {exmpp_stream:get_default_ns(Opening),
exmpp_xml:is_ns_declared_here(Opening, ?NS_DIALBACK),
+ exmpp_stream:get_receiving_entity(Opening),
exmpp_stream:get_version(Opening) == {1, 0}} of
- {?NS_JABBER_SERVER, _, true} when
+ {?NS_JABBER_SERVER, _, Server, true} when
StateData#state.tls and (not StateData#state.authenticated) ->
Opening_Reply = exmpp_stream:opening_reply(Opening,
StateData#state.streamid),
@@ -188,17 +189,25 @@ wait_for_stream({xmlstreamstart, Opening}, StateData) ->
true ->
[exmpp_server_tls:feature()]
end,
- send_element(StateData, exmpp_stream:features(SASL ++ StartTLS)),
+ Features = SASL ++ StartTLS ++ ejabberd_hooks:run_fold(
+ c2s_stream_features,
+ Server,
+ [], [Server]),
+ send_element(StateData, exmpp_stream:features(Features)),
{next_state, wait_for_feature_request, StateData};
- {?NS_JABBER_SERVER, _, true} when
+ {?NS_JABBER_SERVER, _, Server, true} when
StateData#state.authenticated ->
Opening_Reply = exmpp_stream:opening_reply(Opening,
StateData#state.streamid),
send_element(StateData,
exmpp_stream:set_dialback_support(Opening_Reply)),
- send_element(StateData, exmpp_stream:features([])),
+ Features = ejabberd_hooks:run_fold(
+ c2s_stream_features,
+ Server,
+ [], [Server]),
+ send_element(StateData, exmpp_stream:features(Features)),
{next_state, stream_established, StateData};
- {?NS_JABBER_SERVER, true, _} ->
+ {?NS_JABBER_SERVER, true, _Server, _} ->
Opening_Reply = exmpp_stream:opening_reply(Opening,
StateData#state.streamid),
send_element(StateData,
diff --git a/src/mod_caps.erl b/src/mod_caps.erl
index 6af93a6b3..90b51396b 100644
--- a/src/mod_caps.erl
+++ b/src/mod_caps.erl
@@ -33,6 +33,10 @@
-behaviour(gen_mod).
-export([read_caps/1,
+ caps_stream_features/2,
+ disco_features/5,
+ disco_identity/5,
+ disco_info/5,
get_features/1]).
%% gen_mod callbacks
@@ -146,6 +150,42 @@ user_send_packet(From, To, #xmlel{name = 'presence', attrs = Attrs, children = E
user_send_packet(_From, _To, _Packet) ->
ok.
+caps_stream_features(Acc, MyHost) ->
+ case make_my_disco_hash(MyHost) of
+ "" ->
+ Acc;
+ Hash ->
+ [#xmlel{name = c,
+ ns = ?NS_CAPS,
+ attrs = [?XMLATTR(hash, "sha-1"),
+ ?XMLATTR(node, ?EJABBERD_URI),
+ ?XMLATTR(ver, Hash)]} | Acc]
+ end.
+
+disco_features(_Acc, From, To, <<?EJABBERD_URI, $#, _/binary>>, Lang) ->
+ ejabberd_hooks:run_fold(disco_local_features,
+ exmpp_jid:domain(To),
+ empty,
+ [From, To, <<>>, Lang]);
+disco_features(Acc, _From, _To, _Node, _Lang) ->
+ Acc.
+
+disco_identity(_Acc, From, To, <<?EJABBERD_URI, $#, _/binary>>, Lang) ->
+ ejabberd_hooks:run_fold(disco_local_identity,
+ exmpp_jid:domain(To),
+ [],
+ [From, To, <<>>, Lang]);
+disco_identity(Acc, _From, _To, _Node, _Lang) ->
+ Acc.
+
+disco_info(_Acc, Host, Module, <<?EJABBERD_URI, $#, _/binary>>, Lang) ->
+ ejabberd_hooks:run_fold(disco_info,
+ list_to_binary(Host),
+ [],
+ [Host, Module, <<>>, Lang]);
+disco_info(Acc, _Host, _Module, _Node, _Lang) ->
+ Acc.
+
%%====================================================================
%% gen_server callbacks
%%====================================================================
@@ -158,6 +198,16 @@ init([Host, _Opts]) ->
mnesia:add_table_copy(caps_features, node(), disc_copies),
HostB = list_to_binary(Host),
ejabberd_hooks:add(user_send_packet, HostB, ?MODULE, user_send_packet, 75),
+ ejabberd_hooks:add(c2s_stream_features, HostB,
+ ?MODULE, caps_stream_features, 75),
+ ejabberd_hooks:add(s2s_stream_features, HostB,
+ ?MODULE, caps_stream_features, 75),
+ ejabberd_hooks:add(disco_local_features, HostB,
+ ?MODULE, disco_features, 75),
+ ejabberd_hooks:add(disco_local_identity, HostB,
+ ?MODULE, disco_identity, 75),
+ ejabberd_hooks:add(disco_info, HostB,
+ ?MODULE, disco_info, 75),
{ok, #state{host = Host}}.
handle_call(stop, _From, State) ->
@@ -174,6 +224,16 @@ handle_info(_Info, State) ->
terminate(_Reason, State) ->
HostB = list_to_binary(State#state.host),
ejabberd_hooks:delete(user_send_packet, HostB, ?MODULE, user_send_packet, 75),
+ ejabberd_hooks:delete(c2s_stream_features, HostB,
+ ?MODULE, caps_stream_features, 75),
+ ejabberd_hooks:delete(s2s_stream_features, HostB,
+ ?MODULE, caps_stream_features, 75),
+ ejabberd_hooks:delete(disco_local_features, HostB,
+ ?MODULE, disco_features, 75),
+ ejabberd_hooks:delete(disco_local_identity, HostB,
+ ?MODULE, disco_identity, 75),
+ ejabberd_hooks:delete(disco_info, HostB,
+ ?MODULE, disco_info, 75),
ok.
code_change(_OldVsn, State, _Extra) ->
@@ -229,3 +289,104 @@ node_to_binary(Node, SubNode) ->
features_to_binary(L) -> [list_to_binary(I) || I <- L].
binary_to_features(L) -> [binary_to_list(I) || I <- L].
+
+make_my_disco_hash(Host) ->
+ JID = exmpp_jid:make(Host),
+ case {ejabberd_hooks:run_fold(disco_local_features,
+ Host,
+ empty,
+ [JID, JID, <<>>, <<>>]),
+ ejabberd_hooks:run_fold(disco_local_identity,
+ Host,
+ [],
+ [JID, JID, <<>>, <<>>]),
+ ejabberd_hooks:run_fold(disco_info,
+ Host,
+ [],
+ [Host, undefined, <<>>, <<>>])} of
+ {{result, Features}, Identities, Info} ->
+ Feats = lists:map(
+ fun({{Feat, _Host}}) ->
+ #xmlel{name = feature,
+ attrs = [?XMLATTR(var, Feat)]};
+ (Feat) ->
+ #xmlel{name = feature,
+ attrs = [?XMLATTR(var, Feat)]}
+ end, Features),
+ make_disco_hash(Identities ++ Info ++ Feats, sha1);
+ _Err ->
+ ""
+ end.
+
+make_disco_hash(DiscoEls, Algo) when Algo == sha1; Algo == md5 ->
+ Concat = [concat_identities(DiscoEls),
+ concat_features(DiscoEls),
+ concat_info(DiscoEls)],
+ base64:encode_to_string(
+ if Algo == sha1 ->
+ crypto:sha(Concat);
+ Algo == md5 ->
+ crypto:md5(Concat)
+ end).
+
+concat_features(Els) ->
+ lists:usort(
+ lists:flatmap(
+ fun(#xmlel{name = feature} = El) ->
+ [[exmpp_xml:get_attribute(El, var, <<>>), $<]];
+ (_) ->
+ []
+ end, Els)).
+
+concat_identities(Els) ->
+ lists:sort(
+ lists:flatmap(
+ fun(#xmlel{name = identity} = El) ->
+ [[exmpp_xml:get_attribute_as_binary(El, category, <<>>), $/,
+ exmpp_xml:get_attribute_as_binary(El, type, <<>>), $/,
+ exmpp_xml:get_attribute_as_binary(El, lang, <<>>), $/,
+ exmpp_xml:get_attribute_as_binary(El, name, <<>>), $<]];
+ (_) ->
+ []
+ end, Els)).
+
+concat_info(Els) ->
+ lists:sort(
+ lists:flatmap(
+ fun(#xmlel{name = x, ns = ?NS_DATA_FORMS, children = Fields} = El) ->
+ case exmpp_xml:get_attribute_as_list(El, 'type', "") of
+ "result" ->
+ [concat_xdata_fields(Fields)];
+ _ ->
+ []
+ end;
+ (_) ->
+ []
+ end, Els)).
+
+concat_xdata_fields(Fields) ->
+ [Form, Res] =
+ lists:foldl(
+ fun(#xmlel{name = field, children = Els} = El,
+ [FormType, VarFields] = Acc) ->
+ case exmpp_xml:get_attribute_as_binary(El, var, <<>>) of
+ <<>> ->
+ Acc;
+ <<"FORM_TYPE">> ->
+ [exmpp_xml:get_path(
+ El, [{element, value}, cdata]), VarFields];
+ Var ->
+ [FormType,
+ [[[Var, $<],
+ lists:sort(
+ lists:flatmap(
+ fun(#xmlel{name = value} = VEl) ->
+ [[exmpp_xml:get_cdata(VEl), $<]];
+ (_) ->
+ []
+ end, Els))] | VarFields]]
+ end;
+ (_, Acc) ->
+ Acc
+ end, [<<>>, []], Fields),
+ [Form, $<, lists:sort(Res)].
diff --git a/src/mod_disco.erl b/src/mod_disco.erl
index f97ce5dad..01a818866 100644
--- a/src/mod_disco.erl
+++ b/src/mod_disco.erl
@@ -164,9 +164,8 @@ process_local_iq_info(From, To, #iq{type = get, payload = SubEl,
_ -> [?XMLATTR('node', Node)]
end,
Result = #xmlel{ns = ?NS_DISCO_INFO, name = 'query',
- attrs = ANode,
- children = Identity ++ Info ++ lists:map(fun feature_to_xml/1,
- Features)},
+ attrs = ANode,
+ children = Identity ++ Info ++ features_to_xml(Features)},
exmpp_iq:result(IQ_Rec, Result);
{error, Error} ->
exmpp_iq:error(IQ_Rec, Error)
@@ -204,19 +203,24 @@ get_local_features(Acc, _From, _To, _Node, _Lang) ->
{error, 'item-not-found'}
end.
-
-feature_to_xml({{Feature, _Host}}) ->
- feature_to_xml(Feature);
-
-feature_to_xml(Feature) when is_binary(Feature) ->
- #xmlel{ns = ?NS_DISCO_INFO, name = 'feature', attrs = [
- ?XMLATTR('var', Feature)
- ]};
+features_to_xml(FeatureList) ->
+ %% Avoid duplicating features
+ [#xmlel{ns = ?NS_DISCO_INFO, name = 'feature',
+ attrs = [?XMLATTR('var', Feat)]} ||
+ Feat <- lists:usort(
+ lists:map(
+ fun({{Feature, _Host}}) ->
+ feature_to_xml(Feature);
+ (Feature) ->
+ feature_to_xml(Feature)
+ end, FeatureList))].
feature_to_xml(Feature) when is_list(Feature) ->
feature_to_xml(list_to_binary(Feature));
feature_to_xml(Feature) when is_atom(Feature) ->
- feature_to_xml(atom_to_list(Feature)).
+ feature_to_xml(atom_to_list(Feature));
+feature_to_xml(Feature) when is_binary(Feature) ->
+ Feature.
domain_to_xml({Domain}) ->
domain_to_xml(Domain);
@@ -364,9 +368,8 @@ process_sm_iq_info(From, To, #iq{type = get, payload = SubEl,
_ -> [?XMLATTR('node', Node)]
end,
Result = #xmlel{ns = ?NS_DISCO_INFO, name = 'query',
- attrs = ANode,
- children = Identity ++ lists:map(fun feature_to_xml/1,
- Features)},
+ attrs = ANode,
+ children = Identity ++ features_to_xml(Features)},
exmpp_iq:result(IQ_Rec, Result);
{error, Error} ->
exmpp_iq:error(IQ_Rec, Error)
@@ -421,7 +424,11 @@ get_user_resources(JID) ->
%%% Support for: XEP-0157 Contact Addresses for XMPP Services
-get_info(Acc, Host, Module, Node, _Lang) when Node == <<>> ->
+get_info(Acc, Host, Mod, Node, _Lang) when Node == <<>> ->
+ Module = case Mod of
+ undefined -> ?MODULE;
+ _ -> Mod
+ end,
Serverinfo_fields = get_fields_xml(Host, Module),
CData1 = #xmlcdata{cdata = list_to_binary(?NS_SERVERINFO_s)},
Value1 = #xmlel{name = 'value', children = [CData1]},
@@ -437,8 +444,8 @@ get_info(Acc, Host, Module, Node, _Lang) when Node == <<>> ->
},
[X | Acc];
-get_info(_, _, _, _Node, _) ->
- [].
+get_info(Acc, _, _, _Node, _) ->
+ Acc.
get_fields_xml(Host, Module) ->
Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info, []),
diff --git a/src/mod_register.erl b/src/mod_register.erl
index 324b7081f..d885e0904 100644
--- a/src/mod_register.erl
+++ b/src/mod_register.erl
@@ -31,7 +31,7 @@
-export([start/2,
stop/1,
- stream_feature_register/1,
+ stream_feature_register/2,
unauthenticated_iq_register/4,
try_register/5,
process_iq/3]).
@@ -68,7 +68,7 @@ stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_INBAND_REGISTER).
-stream_feature_register(Acc) ->
+stream_feature_register(Acc, _Host) ->
[#xmlel{ns = ?NS_INBAND_REGISTER_FEAT, name = 'register'} | Acc].
unauthenticated_iq_register(_Acc,