./ct_report/coverage/mongoose_disco.COVER.html

1 %%% @doc This module is responsible for running disco_* hooks that collect information
2 %%% for service discovery. It also contains helpers for direct construction of XML elements
3 %%% representing the advertised services.
4
5 -module(mongoose_disco).
6
7 %% Hooks wrappers
8 -export([get_local_identity/5,
9 get_sm_identity/5,
10 get_local_items/5,
11 get_sm_items/5,
12 get_local_features/5,
13 get_sm_features/5,
14 get_muc_features/6,
15 get_info/4]).
16
17 %% Helpers used by hook handlers for Acc manipulation
18 -export([add_identities/2,
19 add_items/2,
20 add_features/2,
21 add_info/2]).
22
23 %% XML construction helpers
24 -export([identities_to_xml/1,
25 items_to_xml/1,
26 features_to_xml/1,
27 info_list_to_xml/1]).
28
29 -ignore_xref([items_to_xml/1]).
30
31 -include("jlib.hrl").
32
33 -type feature_acc() :: acc(feature()).
34 -type feature() :: binary().
35
36 -type item_acc() :: acc(item()).
37 -type item() :: #{jid := jid:lserver(), name => binary(), node => binary()}.
38
39 -type identity_acc() :: acc(identity()).
40 -type identity() :: #{category := binary(), type := binary(), name => binary()}.
41
42 -type info_acc() :: #{host_type := mongooseim:host_type(),
43 module := module(),
44 node := binary(),
45 lang := ejabberd:lang(),
46 result := empty | [info()]}.
47 -type info() :: #{xmlns := binary(), fields := [info_field()]}.
48 -type info_field() :: #{var := binary(), values := [binary()], label => binary()}.
49
50 -type acc(Elem) :: #{host_type := mongooseim:host_type(),
51 from_jid := jid:jid(),
52 to_jid := jid:jid(),
53 node := binary(),
54 lang := ejabberd:lang(),
55 result := empty | [Elem]}.
56
57 -export_type([item_acc/0, feature_acc/0, identity_acc/0,
58 item/0, feature/0, identity/0,
59 info_field/0, info/0, info_acc/0]).
60
61 %% Hook wrapper API
62
63 -spec get_local_identity(mongooseim:host_type(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) ->
64 [exml:element()].
65 get_local_identity(HostType, From, To, Node, Lang) ->
66 221 InitialAcc = new_acc(HostType, From, To, Node, Lang),
67 221 FinalAcc = mongoose_hooks:disco_local_identity(InitialAcc),
68 221 Identities = extract_result(FinalAcc),
69 221 identities_to_xml(Identities).
70
71 -spec get_sm_identity(mongooseim:host_type(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) ->
72 [exml:element()].
73 get_sm_identity(HostType, From, To, Node, Lang) ->
74 12 InitialAcc = new_acc(HostType, From, To, Node, Lang),
75 12 IdentityAcc = mongoose_hooks:disco_sm_identity(InitialAcc),
76 12 Identities = extract_result(IdentityAcc),
77 12 identities_to_xml(Identities).
78
79 -spec get_local_items(mongooseim:host_type(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) ->
80 {result, [exml:element()]} | empty.
81 get_local_items(HostType, From, To, Node, Lang) ->
82 31 Acc = new_acc(HostType, From, To, Node, Lang),
83 31 case mongoose_hooks:disco_local_items(Acc) of
84
:-(
#{result := empty} -> empty;
85 31 #{result := Items} -> {result, items_to_xml(Items)}
86 end.
87
88 -spec get_sm_items(mongooseim:host_type(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) ->
89 {result, [exml:element()]} | empty.
90 get_sm_items(HostType, From, To, Node, Lang) ->
91 13 Acc = new_acc(HostType, From, To, Node, Lang),
92 13 case mongoose_hooks:disco_sm_items(Acc) of
93 6 #{result := empty} -> empty;
94 7 #{result := Items} -> {result, items_to_xml(Items)}
95 end.
96
97 -spec get_local_features(mongooseim:host_type(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) ->
98 {result, [exml:element()]} | empty.
99 get_local_features(HostType, From, To, Node, Lang) ->
100 221 Acc = new_acc(HostType, From, To, Node, Lang),
101 221 case mongoose_hooks:disco_local_features(Acc) of
102
:-(
#{result := empty} -> empty;
103 221 #{result := Features} -> {result, features_to_xml(Features)}
104 end.
105
106 -spec get_sm_features(mongooseim:host_type(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) ->
107 {result, [exml:element()]} | empty.
108 get_sm_features(HostType, From, To, Node, Lang) ->
109 12 Acc = new_acc(HostType, From, To, Node, Lang),
110 12 case mongoose_hooks:disco_sm_features(Acc) of
111
:-(
#{result := empty} -> empty;
112 12 #{result := Features} -> {result, features_to_xml(Features)}
113 end.
114
115 -spec get_muc_features(mongooseim:host_type(), jid:jid(), jid:jid(), binary(), ejabberd:lang(),
116 [mongoose_disco:feature()]) ->
117 [exml:element()].
118 get_muc_features(HostType, From, To, Node, Lang, ExtraFeatures) ->
119 9 InitialAcc = new_acc(HostType, From, To, Node, Lang),
120 9 FinalAcc = mongoose_hooks:disco_muc_features(InitialAcc),
121 9 Features = ExtraFeatures ++ extract_result(FinalAcc),
122 9 features_to_xml(Features).
123
124 -spec get_info(mongooseim:host_type(), module() , binary(), ejabberd:lang()) -> [exml:element()].
125 get_info(HostType, Module, Node, Lang) ->
126 227 InitialAcc = new_info_acc(HostType, Module, Node, Lang),
127 227 FinalAcc = mongoose_hooks:disco_info(InitialAcc),
128 227 InfoList = extract_result(FinalAcc),
129 227 info_list_to_xml(InfoList).
130
131 %% Helper API
132
133 -spec add_identities([identity()], identity_acc()) -> identity_acc().
134 add_identities(Identities, Acc) ->
135 235 add(Identities, Acc).
136
137 -spec add_items([item()], item_acc()) -> item_acc().
138 add_items(Items, Acc) ->
139 84 add(Items, Acc).
140
141 -spec add_features([feature()], feature_acc()) -> feature_acc().
142 add_features(Features, Acc) ->
143 1293 add(Features, Acc).
144
145 -spec add_info([info()], info_acc()) -> info_acc().
146 add_info(InfoList, Acc) ->
147 222 add(InfoList, Acc).
148
149 %% XML construction API
150
151 -spec identities_to_xml([identity()]) -> [exml:element()].
152 identities_to_xml(Identities) ->
153 249 lists:map(fun identity_to_xml/1, Identities).
154
155 -spec items_to_xml([item()]) -> [exml:element()].
156 items_to_xml(Items) ->
157 %% For each JID, leave only the rightmost item with that JID (the one which was added first).
158 %% This is needed as extension modules might add more detailed information about an item
159 %% than the default which is obtained from the registered routes and contains only the JID.
160 38 maps:values(maps:from_list([{JID, item_to_xml(Item)} || #{jid := JID} = Item <- Items])).
161
162 -spec features_to_xml([feature()]) -> [exml:element()].
163 features_to_xml(Features) ->
164 %% Avoid duplicating features
165 249 [feature_to_xml(Feature) || Feature <- lists:usort(Features)].
166
167 -spec info_list_to_xml([info()]) -> [exml:element()].
168 info_list_to_xml(InfoList) ->
169 231 [info_to_xml(Info) || Info <- InfoList].
170
171 %% Acc manipulation
172
173 -spec new_acc(mongooseim:host_type(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) ->
174 acc(any()).
175 new_acc(HostType, From, To, Node, Lang) ->
176 519 #{host_type => HostType,
177 from_jid => From,
178 to_jid => To,
179 node => Node,
180 lang => Lang,
181 result => empty}.
182
183 -spec new_info_acc(mongooseim:host_type(), module(), binary(), ejabberd:lang()) -> info_acc().
184 new_info_acc(HostType, Module, Node, Lang) ->
185 227 #{host_type => HostType,
186 module => Module,
187 node => Node,
188 lang => Lang,
189 result => empty}.
190
191 -spec add([Elem], acc(Elem)) -> acc(Elem);
192 ([info()], info_acc()) -> info_acc().
193 add(Elements, Acc = #{result := empty}) ->
194 719 Acc#{result := Elements};
195 add(Elements, Acc = #{result := InitialElements}) ->
196 1115 Acc#{result := Elements ++ InitialElements}.
197
198 -spec extract_result(acc(Elem)) -> [Elem];
199 (info_acc()) -> [info()].
200 15 extract_result(#{result := empty}) -> [];
201 454 extract_result(#{result := Elements}) -> Elements.
202
203 %% Conversion to XML
204
205 feature_to_xml(Feature) when is_binary(Feature) ->
206 8226 #xmlel{name = <<"feature">>, attrs = [{<<"var">>, Feature}]}.
207
208 item_to_xml(Item) ->
209 86 #xmlel{name = <<"item">>,
210 107 attrs = lists:map(fun({Key, Value}) -> {atom_to_binary(Key, utf8), Value} end,
211 maps:to_list(Item))}.
212
213 identity_to_xml(Identity) ->
214 253 #xmlel{name = <<"identity">>,
215 744 attrs = lists:map(fun({Key, Value}) -> {atom_to_binary(Key, utf8), Value} end,
216 maps:to_list(Identity))}.
217
218 -spec info_to_xml(info()) -> exml:element().
219 info_to_xml(#{xmlns := NS, fields := Fields}) ->
220 226 mongoose_data_forms:form(#{type => <<"result">>, ns => NS, fields => Fields}).
Line Hits Source