./ct_report/coverage/mod_roster.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : mod_roster.erl
3 %%% Author : Alexey Shchepin <alexey@process-one.net>
4 %%% Purpose : Roster management
5 %%% Created : 11 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
6 %%%
7 %%%
8 %%% ejabberd, Copyright (C) 2002-2013 ProcessOne
9 %%%
10 %%% This program is free software; you can redistribute it and/or
11 %%% modify it under the terms of the GNU General Public License as
12 %%% published by the Free Software Foundation; either version 2 of the
13 %%% License, or (at your option) any later version.
14 %%%
15 %%% This program is distributed in the hope that it will be useful,
16 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 %%% General Public License for more details.
19 %%%
20 %%% You should have received a copy of the GNU General Public License
21 %%% along with this program; if not, write to the Free Software
22 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 %%%
24 %%%----------------------------------------------------------------------
25
26 %%% @doc Roster management.
27 %%%
28 %%% Includes support for XEP-0237: Roster Versioning.
29 %%% The roster versioning follows an all-or-nothing strategy:
30 %%% - If the version supplied by the client is the latest, return an empty response.
31 %%% - If not, return the entire new roster (with updated version string).
32 %%% Roster version is a hash digest of the entire roster.
33 %%% No additional data is stored in DB.
34
35 -module(mod_roster).
36 -author('alexey@process-one.net').
37 -xep([{xep, 237}, {version, "1.3"}]).
38 -xep([{xep, 83}, {version, "1.0"}]).
39 -xep([{xep, 93}, {version, "1.2"}]).
40 -behaviour(gen_mod).
41 -behaviour(mongoose_module_metrics).
42
43 -export([start/2,
44 stop/1,
45 hooks/1,
46 config_spec/0,
47 supported_features/0,
48 process_iq/5,
49 get_roster_entry/4,
50 item_to_map/1,
51 set_items/3,
52 set_roster_entry/5,
53 remove_from_roster/3,
54 item_to_xml/1,
55 broadcast_item/3
56 ]).
57
58 % Hook handlers
59 -export([
60 get_user_roster/3,
61 in_subscription/3,
62 out_subscription/3,
63 get_subscription_lists/3,
64 get_jid_info/3,
65 remove_user/3,
66 remove_domain/3,
67 get_versioning_feature/3,
68 get_personal_data/3
69 ]).
70
71 -export([transaction/2,
72 process_subscription_t/6,
73 get_user_rosters_length/2]). % for testing
74
75 -export([config_metrics/1]).
76
77 -ignore_xref([get_user_rosters_length/2,
78 item_to_xml/1,
79 process_subscription_t/6,
80 transaction/2,
81 broadcast_item/3,
82 set_items/3
83 ]).
84
85 -include("mongoose.hrl").
86 -include("jlib.hrl").
87 -include("mod_roster.hrl").
88 -include("mongoose_config_spec.hrl").
89
90 -export_type([roster/0, sub_presence/0, subscription_state/0]).
91
92 -type roster() :: #roster{}.
93
94 -type sub_presence() :: subscribe | subscribed | unsubscribe | unsubscribed.
95
96 -type subscription_state() :: none | from | to | both | remove.
97
98 -type get_user_roster_strategy() :: db_versioning | hash_versioning | no_versioning.
99
100 %% Types used in the backend API
101
102 -type contact() :: jid:simple_jid().
103 -type transaction_state() :: in_transaction | no_transaction.
104 -type entry_format() :: full | short.
105 -type version() :: binary().
106
107 -export_type([contact/0, transaction_state/0, entry_format/0, version/0]).
108
109 %%--------------------------------------------------------------------
110 %% gdpr callback
111 %%--------------------------------------------------------------------
112
113 -spec get_personal_data(Acc, Params, Extra) -> {ok, Acc} when
114 Acc :: gdpr:personal_data(),
115 Params :: #{jid := jid:jid()},
116 Extra :: gen_hook:extra().
117 get_personal_data(Acc, #{jid := #jid{luser = LUser, lserver = LServer}}, #{host_type := HostType}) ->
118 87 Schema = ["jid", "name", "subscription", "ask", "groups", "askmessage", "xs"],
119 87 Records = get_roster(HostType, LUser, LServer),
120 87 SerializedRecords = lists:map(fun roster_record_to_gdpr_entry/1, Records),
121 87 {ok, [{roster, Schema, SerializedRecords} | Acc]}.
122
123 -spec roster_record_to_gdpr_entry(roster()) -> gdpr:entry().
124 roster_record_to_gdpr_entry(#roster{ jid = JID, name = Name,
125 subscription = Subscription, ask = Ask, groups = Groups,
126 askmessage = AskMessage, xs = XS }) ->
127 2 [
128 jid:to_binary(JID),
129 Name,
130 atom_to_binary(Subscription, utf8),
131 atom_to_binary(Ask, utf8),
132
:-(
string:join([ unicode:characters_to_list(G) || G <- Groups ], ", "),
133 AskMessage,
134
:-(
<< (exml:to_binary(X)) || X <- XS >>
135 ].
136
137 %%--------------------------------------------------------------------
138 %% mod_roster's callbacks
139 %%--------------------------------------------------------------------
140
141 -spec start(mongooseim:host_type(), gen_mod:module_opts()) -> any().
142 start(HostType, Opts = #{iqdisc := IQDisc}) ->
143 366 mod_roster_backend:init(HostType, Opts),
144 366 gen_iq_handler:add_iq_handler_for_domain(HostType, ?NS_ROSTER, ejabberd_sm,
145 fun ?MODULE:process_iq/5, #{}, IQDisc).
146
147 -spec stop(mongooseim:host_type()) -> any().
148 stop(HostType) ->
149 356 gen_iq_handler:remove_iq_handler_for_domain(HostType, ?NS_ROSTER, ejabberd_sm).
150
151 -spec config_spec() -> mongoose_config_spec:config_section().
152 config_spec() ->
153 166 #section{
154 items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(),
155 <<"versioning">> => #option{type = boolean},
156 <<"store_current_id">> => #option{type = boolean},
157 <<"backend">> => #option{type = atom,
158 validate = {module, mod_roster}}
159 },
160 defaults = #{<<"iqdisc">> => one_queue,
161 <<"versioning">> => false,
162 <<"store_current_id">> => false,
163 <<"backend">> => mnesia}
164 }.
165
166 162 supported_features() -> [dynamic_domains].
167
168 hooks(HostType) ->
169 722 [{roster_get, HostType, fun ?MODULE:get_user_roster/3, #{}, 50},
170 {roster_in_subscription, HostType, fun ?MODULE:in_subscription/3, #{}, 50},
171 {roster_out_subscription, HostType, fun ?MODULE:out_subscription/3, #{}, 50},
172 {roster_get_subscription_lists, HostType, fun ?MODULE:get_subscription_lists/3, #{}, 50},
173 {roster_get_jid_info, HostType, fun ?MODULE:get_jid_info/3, #{}, 50},
174 {remove_user, HostType, fun ?MODULE:remove_user/3, #{}, 50},
175 {remove_domain, HostType, fun ?MODULE:remove_domain/3, #{}, 50},
176 {anonymous_purge_hook, HostType, fun ?MODULE:remove_user/3, #{}, 50},
177 {roster_get_versioning_feature, HostType, fun ?MODULE:get_versioning_feature/3, #{}, 50},
178 {get_personal_data, HostType, fun ?MODULE:get_personal_data/3, #{}, 50}].
179
180 -spec process_iq(mongoose_acc:t(), jid:jid(), jid:jid(), jlib:iq(), map()) ->
181 {mongoose_acc:t(), jlib:iq()}.
182 process_iq(Acc, From = #jid{lserver = LServer, luser = LUser},
183 To = #jid{lserver = LServer, luser = LUser}, IQ, _Extra) ->
184 57 process_local_iq(Acc, From, To, IQ);
185 process_iq(Acc, _From, _To, IQ = #iq{lang = Lang, sub_el = SubEl}, _Extra) ->
186 %% Error type 'forbidden' is specified in Section 2.3.3 of RFC6121 for iq set.
187 %% The behaviour is unspecified for iq get, but it makes sense to handle them consistently.
188 2 ErrorMsg = <<"It is forbidden to query the roster of another user">>,
189 2 ErrorEl = mongoose_xmpp_errors:forbidden(Lang, ErrorMsg),
190 2 {Acc, IQ#iq{type = error, sub_el = [SubEl, ErrorEl]}}.
191
192 -spec process_local_iq(mongoose_acc:t(), jid:jid(), jid:jid(), jlib:iq()) ->
193 {mongoose_acc:t(), jlib:iq()}.
194 process_local_iq(Acc, From, To, #iq{type = Type} = IQ) ->
195 57 HostType = mongoose_acc:host_type(Acc),
196 57 IQReply = case Type of
197 33 set -> process_iq_set(HostType, From, To, IQ);
198 24 get -> process_iq_get(HostType, From, To, IQ)
199 end,
200 57 {Acc, IQReply}.
201
202 roster_hash(Items) ->
203 4 L = [R#roster{groups = lists:sort(Grs)} ||
204 4 R = #roster{groups = Grs} <- Items],
205 4 mongoose_bin:encode_crypto(term_to_binary(lists:sort(L))).
206
207 -spec roster_versioning_enabled(mongooseim:host_type()) -> boolean().
208 roster_versioning_enabled(HostType) ->
209 7205 gen_mod:get_module_opt(HostType, ?MODULE, versioning, false).
210
211 -spec roster_version_on_db(mongooseim:host_type()) -> boolean().
212 roster_version_on_db(HostType) ->
213 1139 gen_mod:get_module_opt(HostType, ?MODULE, store_current_id, false).
214
215 %% Returns a list that may contain an xmlel with the XEP-237 feature if it's enabled.
216 -spec get_versioning_feature(Acc, Params, Extra) -> {ok, Acc} when
217 Acc :: [exml:element()],
218 Params :: map(),
219 Extra :: gen_hook:extra().
220 get_versioning_feature(Acc, _, #{host_type := HostType}) ->
221 6177 NewAcc = case roster_versioning_enabled(HostType) of
222 true ->
223 6 Feature = #xmlel{name = <<"ver">>,
224 attrs = [{<<"xmlns">>, ?NS_ROSTER_VER}]},
225 6 [Feature | Acc];
226 6171 false -> []
227 end,
228 6177 {ok, NewAcc}.
229
230 roster_version(HostType, #jid{luser = LUser, lserver = LServer} = JID) ->
231 3 case roster_version_on_db(HostType) of
232 true ->
233 2 case read_roster_version(HostType, LUser, LServer) of
234
:-(
error -> not_found;
235 2 V -> V
236 end;
237 false ->
238 1 R = get_roster_old(HostType, JID),
239 1 roster_hash(R)
240 end.
241
242 %% Load roster from DB only if neccesary.
243 %% It is neccesary if
244 %% - roster versioning is disabled in server OR
245 %% - roster versioning is not used by the client OR
246 %% - roster versioning is used by server and client,
247 %% BUT the server isn't storing versions on db OR
248 %% - the roster version from client don't match current version.
249 -spec process_iq_get(mongooseim:host_type(), jid:jid(), jid:jid(), jlib:iq()) -> jlib:iq().
250 process_iq_get(HostType, From, To, #iq{sub_el = SubEl} = IQ) ->
251 24 AttrVer = exml_query:attr(SubEl, <<"ver">>), %% type binary() | undefined
252 24 VersioningRequested = is_binary(AttrVer),
253 24 VersioningEnabled = roster_versioning_enabled(HostType),
254 24 VersionOnDb = roster_version_on_db(HostType),
255 24 Strategy = choose_get_user_roster_strategy(VersioningRequested, VersioningEnabled, VersionOnDb),
256 24 {ItemsToSend, VersionToSend} =
257 get_user_roster_based_on_version(HostType, Strategy, AttrVer, From, To),
258 24 IQ#iq{type = result, sub_el = create_sub_el(ItemsToSend, VersionToSend)}.
259
260 -spec choose_get_user_roster_strategy(VersioningRequested :: boolean(),
261 VersioningEnabled :: boolean(),
262 VersionOnDb :: boolean()) ->
263 get_user_roster_strategy().
264 3 choose_get_user_roster_strategy(true, true, true) -> db_versioning;
265 3 choose_get_user_roster_strategy(true, true, false) -> hash_versioning;
266 18 choose_get_user_roster_strategy(_, _, _) -> no_versioning.
267
268 get_user_roster_based_on_version(HostType, db_versioning, RequestedVersion, From, To) ->
269 3 get_user_roster_db_versioning(HostType, RequestedVersion, From, To);
270 get_user_roster_based_on_version(HostType, hash_versioning, RequestedVersion, From, To) ->
271 3 get_user_roster_hash_versioning(HostType, RequestedVersion, From, To);
272 get_user_roster_based_on_version(HostType, no_versioning, _RequestedVersion, From, To) ->
273 18 get_user_roster_no_versioning(HostType, From, To).
274
275 get_user_roster_db_versioning(HostType, RequestedVersion, From, To)
276 when is_binary(RequestedVersion) ->
277 3 LUser = From#jid.luser,
278 3 LServer = From#jid.lserver,
279 3 case read_roster_version(HostType, LUser, LServer) of
280 error ->
281 1 RosterVersion = write_roster_version(HostType, LUser, LServer),
282 1 {lists:map(fun item_to_xml/1,
283 get_roster_old(HostType, To#jid.lserver, From)),
284 RosterVersion};
285 RequestedVersion ->
286 1 {false, false};
287 NewVersion ->
288 1 {lists:map(fun item_to_xml/1,
289 get_roster_old(HostType, To#jid.lserver, From)),
290 NewVersion}
291 end.
292
293 get_user_roster_hash_versioning(HostType, RequestedVersion, From, To)
294 when is_binary(RequestedVersion) ->
295 3 RosterItems = get_roster_old(HostType, To#jid.lserver, From),
296 3 case roster_hash(RosterItems) of
297 RequestedVersion ->
298 1 {false, false};
299 New ->
300 2 {lists:map(fun item_to_xml/1, RosterItems), New}
301 end.
302
303 get_user_roster_no_versioning(HostType, From, To) ->
304 18 {lists:map(fun item_to_xml/1,
305 get_roster_old(HostType, To#jid.lserver, From)),
306 false}.
307
308 create_sub_el(false, false) ->
309 2 [];
310 create_sub_el(Items, false) ->
311 18 [#xmlel{name = <<"query">>,
312 attrs = [{<<"xmlns">>, ?NS_ROSTER}],
313 children = Items}];
314 create_sub_el(Items, Version) ->
315 4 [#xmlel{name = <<"query">>,
316 attrs = [{<<"xmlns">>, ?NS_ROSTER},
317 {<<"ver">>, Version}],
318 children = Items}].
319
320 -spec get_user_roster(Acc, Params, Extra) -> {ok, Acc} when
321 Acc :: mongoose_acc:t(),
322 Params :: #{jid := jid:jid()},
323 Extra :: gen_hook:extra().
324 get_user_roster(Acc, #{jid := #jid{luser = LUser, lserver = LServer}}, #{host_type := HostType}) ->
325 149 NewAcc = get_user_roster(Acc, LUser, LServer, HostType),
326 149 {ok, NewAcc}.
327
328 -spec get_user_roster(mongoose_acc:t(),
329 jid:luser(),
330 jid:lserver(),
331 mongooseim:host_type()) -> mongoose_acc:t().
332 get_user_roster(Acc, LUser, LServer, HostType) ->
333 5814 case mongoose_acc:get(roster, show_full_roster, false, Acc) of
334 true ->
335 93 Roster = get_roster(HostType, LUser, LServer),
336 93 mongoose_acc:append(roster, items, Roster, Acc);
337 _ ->
338 5721 Roster = lists:filter(fun (#roster{subscription = none, ask = in}) ->
339 3 false;
340 (_) ->
341 233 true
342 end, get_roster(HostType, LUser, LServer)),
343 5721 mongoose_acc:append(roster, items, Roster, Acc)
344 end.
345
346 item_to_xml(Item) ->
347 855 Attrs1 = [{<<"jid">>,
348 jid:to_binary(Item#roster.jid)}],
349 855 Attrs2 = case Item#roster.name of
350 410 <<"">> -> Attrs1;
351 445 Name -> [{<<"name">>, Name} | Attrs1]
352 end,
353 855 Attrs3 = case Item#roster.subscription of
354 407 none -> [{<<"subscription">>, <<"none">>} | Attrs2];
355 126 from -> [{<<"subscription">>, <<"from">>} | Attrs2];
356 134 to -> [{<<"subscription">>, <<"to">>} | Attrs2];
357 164 both -> [{<<"subscription">>, <<"both">>} | Attrs2];
358 24 remove -> [{<<"subscription">>, <<"remove">>} | Attrs2]
359 end,
360 855 Attrs4 = case ask_to_pending(Item#roster.ask) of
361 191 out -> [{<<"ask">>, <<"subscribe">>} | Attrs3];
362 118 both -> [{<<"ask">>, <<"subscribe">>} | Attrs3];
363 546 _ -> Attrs3
364 end,
365 855 SubEls1 = lists:map(fun (G) ->
366 296 #xmlel{name = <<"group">>, attrs = [],
367 children = [{xmlcdata, G}]}
368 end,
369 Item#roster.groups),
370 855 SubEls = SubEls1 ++ Item#roster.xs,
371 855 #xmlel{name = <<"item">>, attrs = Attrs4,
372 children = SubEls}.
373
374 -spec process_iq_set(mongooseim:host_type(), jid:jid(), jid:jid(), jlib:iq()) -> jlib:iq().
375 process_iq_set(HostType, From, To, #iq{sub_el = SubEl} = IQ) ->
376 33 #xmlel{children = Els} = SubEl,
377 33 mongoose_hooks:roster_set(HostType, From, To, SubEl),
378 33 lists:foreach(fun(El) -> process_item_set(HostType, From, To, El) end, Els),
379 33 IQ#iq{type = result, sub_el = []}.
380
381 -spec process_item_set(mongooseim:host_type(), jid:jid(), jid:jid(), exml:element()) -> ok.
382 process_item_set(HostType, From, To, #xmlel{attrs = Attrs} = El) ->
383 33 JID1 = jid:from_binary(xml:get_attr_s(<<"jid">>, Attrs)),
384 33 do_process_item_set(HostType, JID1, From, To, El);
385
:-(
process_item_set(_HostType, _From, _To, _) -> ok.
386
387 -spec do_process_item_set(mongooseim:host_type(), error | jid:jid(), jid:jid(), jid:jid(),
388 exml:element()) -> ok.
389
:-(
do_process_item_set(_, error, _, _, _) -> ok;
390 do_process_item_set(HostType, #jid{} = JID1, #jid{} = From, #jid{} = To,
391 #xmlel{attrs = Attrs, children = Els}) ->
392 33 MakeItem2 = fun(Item) ->
393 33 Item1 = process_item_attrs(Item, Attrs),
394 33 process_item_els(Item1, Els)
395 end,
396 33 set_roster_item(HostType, JID1, From, To, MakeItem2),
397 33 ok.
398
399 %% @doc this is run when a roster item is to be added, updated or removed
400 %% the interface of this func could probably be a bit simpler
401 -spec set_roster_item(mongooseim:host_type(),
402 ContactJID :: jid:jid(),
403 From :: jid:jid(),
404 To :: jid:jid(),
405 fun((roster()) -> roster())) -> ok | {error, any()}.
406 set_roster_item(HostType, ContactJID, From, To, MakeItem) ->
407 186 ContactLJID = jid:to_lower(ContactJID),
408 186 F = fun() -> set_roster_item_t(HostType, From, ContactLJID, MakeItem) end,
409 186 case transaction(HostType, F) of
410 {atomic, does_not_exist} ->
411 11 {error, does_not_exist};
412 {atomic, {OldItem, NewItem}} ->
413 175 push_item(HostType, From, To, NewItem),
414 175 case NewItem#roster.subscription of
415 remove ->
416 24 send_unsubscribing_presence(From, OldItem),
417 24 ok;
418 151 _ -> ok
419 end;
420 E ->
421
:-(
?LOG_ERROR(#{what => roster_set_item_failed, reason => E}),
422
:-(
{error, E}
423 end.
424
425 -spec set_roster_item_t(mongooseim:host_type(), jid:jid(), contact(),
426 fun((roster()) -> roster())) ->
427 does_not_exist | {roster(), roster()}.
428 set_roster_item_t(HostType, UserJid = #jid{luser = LUser, lserver = LServer},
429 ContactLJID, MakeItem) ->
430 186 InitialItem = get_roster_entry_t(HostType, UserJid, ContactLJID, short),
431 186 Item1 = case InitialItem of
432 152 does_not_exist -> new_roster_item(LUser, LServer, ContactLJID);
433 34 Item -> Item
434 end,
435 186 Item2 = MakeItem(Item1),
436 186 case {InitialItem, Item2} of
437 {does_not_exist, #roster{subscription = remove}} ->
438 %% Cannot remove a non-existing item
439 11 does_not_exist;
440 _ ->
441 175 case Item2#roster.subscription of
442 24 remove -> del_roster_t(HostType, LUser, LServer, ContactLJID);
443 151 _ -> update_roster_t(HostType, Item2)
444 end,
445 175 Item3 = mongoose_hooks:roster_process_item(HostType, LServer, Item2),
446 175 case roster_version_on_db(HostType) of
447 2 true -> write_roster_version_t(HostType, LUser, LServer);
448 173 false -> ok
449 end,
450 175 {Item1, Item3}
451 end.
452
453 -spec new_roster_item(jid:luser(), jid:lserver(), jid:simple_jid()) -> roster().
454 new_roster_item(LUser, LServer, ContactLJID) ->
455 152 #roster{usj = {LUser, LServer, ContactLJID},
456 us = {LUser, LServer},
457 jid = ContactLJID}.
458
459 process_item_attrs(Item, [{<<"jid">>, Val} | Attrs]) ->
460 33 case jid:from_binary(Val) of
461 error ->
462
:-(
process_item_attrs(Item, Attrs);
463 JID1 ->
464 33 JID = jid:to_lower(JID1),
465 33 process_item_attrs(Item#roster{jid = JID}, Attrs)
466 end;
467 process_item_attrs(Item, [{<<"name">>, Val} | Attrs]) ->
468 31 process_item_attrs(Item#roster{name = Val}, Attrs);
469 process_item_attrs(Item, [{<<"subscription">>, <<"remove">>} | Attrs]) ->
470 2 process_item_attrs(Item#roster{subscription = remove}, Attrs);
471 process_item_attrs(Item, [_ | Attrs]) ->
472
:-(
process_item_attrs(Item, Attrs);
473 process_item_attrs(Item, []) ->
474 33 Item.
475
476 process_item_els(Item,
477 [#xmlel{name = Name, attrs = Attrs, children = SEls}
478 | Els]) ->
479 29 case Name of
480 <<"group">> ->
481 29 Groups = [xml:get_cdata(SEls) | Item#roster.groups],
482 29 process_item_els(Item#roster{groups = Groups}, Els);
483 _ ->
484
:-(
case xml:get_attr_s(<<"xmlns">>, Attrs) of
485
:-(
<<"">> -> process_item_els(Item, Els);
486 _ ->
487
:-(
XEls = [#xmlel{name = Name, attrs = Attrs,
488 children = SEls}
489 | Item#roster.xs],
490
:-(
process_item_els(Item#roster{xs = XEls}, Els)
491 end
492 end;
493 process_item_els(Item, [{xmlcdata, _} | Els]) ->
494
:-(
process_item_els(Item, Els);
495 33 process_item_els(Item, []) -> Item.
496
497 push_item(HostType, JID, From, Item) ->
498 1004 broadcast_item(JID, Item#roster.jid, Item#roster.subscription),
499 1004 case roster_versioning_enabled(HostType) of
500 true ->
501 3 push_item_version(JID, From, Item, roster_version(HostType, JID));
502 false ->
503 1001 lists:foreach(fun(Resource) ->
504 842 push_item_without_version(HostType, JID, Resource, From, Item)
505 end,
506 ejabberd_sm:get_user_resources(JID))
507 end.
508
509 -spec broadcast_item(jid:jid(), jid:simple_jid(), subscription_state()) -> ok.
510 broadcast_item(#jid{luser = LUser, lserver = LServer}, ContactJid, Subscription) ->
511 1004 Item = {item, ContactJid, Subscription},
512 1004 UserPids = ejabberd_sm:get_user_present_pids(LUser, LServer),
513 1004 lists:foreach(fun({_, Pid}) -> mongoose_c2s:cast(Pid, ?MODULE, Item) end, UserPids).
514
515 push_item_without_version(HostType, JID, Resource, From, Item) ->
516 842 mongoose_hooks:roster_push(HostType, From, Item),
517 842 push_item_final(jid:replace_resource(JID, Resource), From, Item, not_found).
518
519 push_item_version(JID, From, Item, RosterVersion) ->
520 3 lists:foreach(fun(Resource) ->
521 3 push_item_final(jid:replace_resource(JID, Resource),
522 From, Item, RosterVersion)
523 end, ejabberd_sm:get_user_resources(JID)).
524
525 push_item_final(JID, From, Item, RosterVersion) ->
526 845 ExtraAttrs = case RosterVersion of
527 842 not_found -> [];
528 3 _ -> [{<<"ver">>, RosterVersion}]
529 end,
530 845 ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
531 %% @doc Roster push, calculate and include the version attribute.
532 %% TODO: don't push to those who didn't load roster
533 id = <<"push", (mongoose_bin:gen_from_crypto())/binary>>,
534 sub_el =
535 [#xmlel{name = <<"query">>,
536 attrs = [{<<"xmlns">>, ?NS_ROSTER} | ExtraAttrs],
537 children = [item_to_xml(Item)]}]},
538 845 ejabberd_router:route(From, JID, jlib:iq_to_xml(ResIQ)).
539
540 -spec get_subscription_lists(Acc, Params, Extra) -> {ok, Acc} when
541 Acc :: mongoose_acc:t(),
542 Params :: #{jid := jid:jid()},
543 Extra :: gen_hook:extra().
544 get_subscription_lists(Acc, #{jid := #jid{luser = LUser, lserver = LServer} = JID}, _) ->
545 5465 Items = mod_roster_backend:get_subscription_lists(Acc, LUser, LServer),
546 5465 SubLists = fill_subscription_lists(JID, LServer, Items, [], [], []),
547 5465 {ok, mongoose_acc:set(roster, subscription_lists, SubLists, Acc)}.
548
549 fill_subscription_lists(JID, LServer, [#roster{} = I | Is], F, T, P) ->
550 13 J = element(3, I#roster.usj),
551 13 NewP = build_pending(I, JID, P),
552 13 case I#roster.subscription of
553 both ->
554 7 fill_subscription_lists(JID, LServer, Is, [J | F], [J | T], NewP);
555 from ->
556 3 fill_subscription_lists(JID, LServer, Is, [J | F], T, NewP);
557
:-(
to -> fill_subscription_lists(JID, LServer, Is, F, [J | T], NewP);
558 3 _ -> fill_subscription_lists(JID, LServer, Is, F, T, NewP)
559 end;
560 5465 fill_subscription_lists(_, _LServer, [], F, T, P) -> {F, T, P}.
561
562 build_pending(#roster{ask = Ask} = I, JID, P)
563 when Ask == in; Ask == both ->
564 3 Status = case I#roster.askmessage of
565 3 Message when is_binary(Message) -> Message;
566
:-(
true -> <<>>
567 end,
568 3 StatusEl = #xmlel{
569 name = <<"status">>,
570 children = [#xmlcdata{content = Status}]},
571 3 El = #xmlel{
572 name = <<"presence">>,
573 attrs = [{<<"from">>, jid:to_binary(I#roster.jid)},
574 {<<"to">>, jid:to_binary(JID)},
575 {<<"type">>, <<"subscribe">>}],
576 children = [StatusEl]},
577 3 [El | P];
578 build_pending(_, _, P) ->
579 10 P.
580
581
:-(
ask_to_pending(subscribe) -> out;
582
:-(
ask_to_pending(unsubscribe) -> none;
583 855 ask_to_pending(Ask) -> Ask.
584
585 -spec in_subscription(Acc, Params, Extra) -> {ok, Acc} when
586 Acc :: mongoose_acc:t(),
587 Params :: #{to := jid:jid(),
588 from := jid:jid(),
589 type := sub_presence(),
590 reason := iodata()},
591 Extra :: gen_hook:extra().
592 in_subscription(Acc,
593 #{to := ToJID, from := FromJID, type := Type, reason := Reason},
594 #{host_type := HostType}) ->
595 570 Res = process_subscription(HostType, in, ToJID, FromJID, Type, Reason),
596 570 {ok, mongoose_acc:set(hook, result, Res, Acc)}.
597
598 -spec out_subscription(Acc, Params, Extra) -> {ok, Acc} when
599 Acc :: mongoose_acc:t(),
600 Params :: #{to := jid:jid(),
601 from := jid:jid(),
602 type := sub_presence()},
603 Extra :: gen_hook:extra().
604 out_subscription(Acc,
605 #{to := ToJID, from := FromJID, type := Type},
606 #{host_type := HostType}) ->
607 388 Res = process_subscription(HostType, out, FromJID, ToJID, Type, <<>>),
608 388 {ok, mongoose_acc:set(hook, result, Res, Acc)}.
609
610 -spec process_subscription(mongooseim:host_type(), in | out, jid:jid(), jid:jid(),
611 sub_presence(), iodata()) ->
612 boolean().
613 process_subscription(HostType, Direction, JID, ContactJID, Type, Reason) ->
614 958 TransactionFun =
615 fun() ->
616 958 process_subscription_t(HostType, Direction, JID, ContactJID, Type, Reason)
617 end,
618 958 case transaction(HostType, TransactionFun) of
619 {atomic, {Push, AutoReply}} ->
620 958 case AutoReply of
621 866 none -> ok;
622 _ ->
623 92 PresenceStanza = #xmlel{name = <<"presence">>,
624 attrs = [{<<"type">>, autoreply_to_type(AutoReply)}],
625 children = []},
626 92 ejabberd_router:route(JID, ContactJID, PresenceStanza)
627 end,
628 958 case Push of
629 {push, #roster{subscription = none, ask = in}} ->
630 108 true;
631 {push, Item} ->
632 829 push_item(HostType, JID, JID, Item),
633 829 true;
634 21 none -> false
635 end;
636
:-(
_ -> false
637 end.
638
639
:-(
autoreply_to_type(subscribed) -> <<"subscribed">>;
640 92 autoreply_to_type(unsubscribed) -> <<"unsubscribed">>.
641
642 -spec process_subscription_t(mongooseim:host_type(), in | out, jid:jid(), jid:jid(),
643 sub_presence(), iodata()) ->
644 {Push :: none | {push, roster()},
645 AutoReply :: none | subscribed | unsubscribed}.
646 process_subscription_t(HostType, Direction, JID, ContactJID, Type, Reason) ->
647 958 #jid{luser = LUser, lserver = LServer} = JID,
648 958 ContactLJID = jid:to_lower(ContactJID),
649 958 Item = case get_roster_entry_t(HostType, JID, ContactLJID, full) of
650 does_not_exist ->
651 119 #roster{usj = {LUser, LServer, ContactLJID},
652 us = {LUser, LServer},
653 jid = ContactLJID};
654 839 R -> R
655 end,
656 958 NewState = case Direction of
657 out ->
658 388 out_state_change(Item#roster.subscription,
659 Item#roster.ask, Type);
660 in ->
661 570 in_state_change(Item#roster.subscription,
662 Item#roster.ask, Type)
663 end,
664 958 AutoReply = case Direction of
665 388 out -> none;
666 in ->
667 570 in_auto_reply(Item#roster.subscription,
668 Item#roster.ask, Type)
669 end,
670 958 AskMessage = case NewState of
671 118 {_, both} -> Reason;
672 190 {_, in} -> Reason;
673 650 _ -> <<"">>
674 end,
675 958 case NewState of
676 15 none -> {none, AutoReply};
677 {none, none}
678 when Item#roster.subscription == none,
679 Item#roster.ask == in ->
680 6 del_roster_t(HostType, LUser, LServer, ContactLJID), {none, AutoReply};
681 {Subscription, Pending} ->
682 937 NewItem = Item#roster{subscription = Subscription,
683 ask = Pending,
684 askmessage = iolist_to_binary(AskMessage)},
685 937 roster_subscribe_t(HostType, NewItem),
686 937 case roster_version_on_db(HostType) of
687
:-(
true -> write_roster_version_t(HostType, LUser, LServer);
688 937 false -> ok
689 end,
690 937 {{push, NewItem}, AutoReply}
691 end.
692
693 %% in_state_change(Subscription, Pending, Type) -> NewState
694 %% NewState = none | {NewSubscription, NewPending}
695 -ifdef(ROSTER_GATEWAY_WORKAROUND).
696
697 -define(NNSD, {to, none}).
698
699 -define(NISD, {to, in}).
700
701 -else.
702
703 -define(NNSD, none).
704
705 -define(NISD, none).
706
707 -endif.
708
709 108 in_state_change(none, none, subscribe) -> {none, in};
710
:-(
in_state_change(none, none, subscribed) -> ?NNSD;
711 1 in_state_change(none, none, unsubscribe) -> none;
712 11 in_state_change(none, none, unsubscribed) -> none;
713 59 in_state_change(none, out, subscribe) -> {none, both};
714 41 in_state_change(none, out, subscribed) -> {to, none};
715
:-(
in_state_change(none, out, unsubscribe) -> none;
716 6 in_state_change(none, out, unsubscribed) -> {none, none};
717
:-(
in_state_change(none, in, subscribe) -> none;
718
:-(
in_state_change(none, in, subscribed) -> ?NISD;
719
:-(
in_state_change(none, in, unsubscribe) -> {none, none};
720
:-(
in_state_change(none, in, unsubscribed) -> none;
721
:-(
in_state_change(none, both, subscribe) -> none;
722 59 in_state_change(none, both, subscribed) -> {to, in};
723
:-(
in_state_change(none, both, unsubscribe) -> {none, out};
724
:-(
in_state_change(none, both, unsubscribed) -> {none, in};
725 23 in_state_change(to, none, subscribe) -> {to, in};
726
:-(
in_state_change(to, none, subscribed) -> none;
727
:-(
in_state_change(to, none, unsubscribe) -> none;
728 86 in_state_change(to, none, unsubscribed) -> {none, none};
729
:-(
in_state_change(to, in, subscribe) -> none;
730
:-(
in_state_change(to, in, subscribed) -> none;
731
:-(
in_state_change(to, in, unsubscribe) -> {to, none};
732
:-(
in_state_change(to, in, unsubscribed) -> {none, in};
733
:-(
in_state_change(from, none, subscribe) -> none;
734
:-(
in_state_change(from, none, subscribed) -> {both, none};
735 13 in_state_change(from, none, unsubscribe) -> {none, none};
736
:-(
in_state_change(from, none, unsubscribed) -> none;
737
:-(
in_state_change(from, out, subscribe) -> none;
738 82 in_state_change(from, out, subscribed) -> {both, none};
739
:-(
in_state_change(from, out, unsubscribe) -> {none, out};
740
:-(
in_state_change(from, out, unsubscribed) -> {from, none};
741
:-(
in_state_change(both, none, subscribe) -> none;
742
:-(
in_state_change(both, none, subscribed) -> none;
743 79 in_state_change(both, none, unsubscribe) -> {to, none};
744 2 in_state_change(both, none, unsubscribed) -> {from, none}.
745
746 108 out_state_change(none, none, subscribe) -> {none, out};
747 2 out_state_change(none, none, subscribed) -> none;
748 1 out_state_change(none, none, unsubscribe) -> none;
749
:-(
out_state_change(none, none, unsubscribed) -> none;
750 out_state_change(none, out, subscribe) ->
751
:-(
{none, out}; %% We need to resend query (RFC3921, section 9.2)
752
:-(
out_state_change(none, out, subscribed) -> none;
753
:-(
out_state_change(none, out, unsubscribe) -> {none, none};
754
:-(
out_state_change(none, out, unsubscribed) -> none;
755 59 out_state_change(none, in, subscribe) -> {none, both};
756 41 out_state_change(none, in, subscribed) -> {from, none};
757
:-(
out_state_change(none, in, unsubscribe) -> none;
758 6 out_state_change(none, in, unsubscribed) -> {none, none};
759
:-(
out_state_change(none, both, subscribe) -> none;
760 59 out_state_change(none, both, subscribed) -> {from, out};
761
:-(
out_state_change(none, both, unsubscribe) -> {none, in};
762
:-(
out_state_change(none, both, unsubscribed) -> {none, out};
763
:-(
out_state_change(to, none, subscribe) -> none;
764
:-(
out_state_change(to, none, subscribed) -> {both, none};
765 5 out_state_change(to, none, unsubscribe) -> {none, none};
766
:-(
out_state_change(to, none, unsubscribed) -> none;
767
:-(
out_state_change(to, in, subscribe) -> none;
768 82 out_state_change(to, in, subscribed) -> {both, none};
769
:-(
out_state_change(to, in, unsubscribe) -> {none, in};
770
:-(
out_state_change(to, in, unsubscribed) -> {to, none};
771 23 out_state_change(from, none, subscribe) -> {from, out};
772
:-(
out_state_change(from, none, subscribed) -> none;
773
:-(
out_state_change(from, none, unsubscribe) -> none;
774
:-(
out_state_change(from, none, unsubscribed) -> {none, none};
775
:-(
out_state_change(from, out, subscribe) -> none;
776
:-(
out_state_change(from, out, subscribed) -> none;
777
:-(
out_state_change(from, out, unsubscribe) -> {from, none};
778
:-(
out_state_change(from, out, unsubscribed) -> {none, out};
779
:-(
out_state_change(both, none, subscribe) -> none;
780
:-(
out_state_change(both, none, subscribed) -> none;
781
:-(
out_state_change(both, none, unsubscribe) -> {from, none};
782 2 out_state_change(both, none, unsubscribed) -> {to, none}.
783
784
:-(
in_auto_reply(from, none, subscribe) -> subscribed;
785
:-(
in_auto_reply(from, out, subscribe) -> subscribed;
786
:-(
in_auto_reply(both, none, subscribe) -> subscribed;
787
:-(
in_auto_reply(none, in, unsubscribe) -> unsubscribed;
788
:-(
in_auto_reply(none, both, unsubscribe) -> unsubscribed;
789
:-(
in_auto_reply(to, in, unsubscribe) -> unsubscribed;
790 13 in_auto_reply(from, none, unsubscribe) -> unsubscribed;
791
:-(
in_auto_reply(from, out, unsubscribe) -> unsubscribed;
792 79 in_auto_reply(both, none, unsubscribe) -> unsubscribed;
793 478 in_auto_reply(_, _, _) -> none.
794
795 get_user_rosters_length(HostType, JID) ->
796 1 length(get_roster_old(HostType, JID)).
797
798 -spec remove_user(Acc, Params, Extra) -> {ok, Acc} when
799 Acc :: mongoose_acc:t(),
800 Params :: #{jid := jid:jid()},
801 Extra :: gen_hook:extra().
802 remove_user(Acc, #{jid := #jid{luser = LUser, lserver = LServer} = JID}, #{host_type := HostType}) ->
803 5665 Acc1 = try_send_unsubscription_to_rosteritems(Acc, JID),
804 5665 F = fun() -> mod_roster_backend:remove_user_t(HostType, LUser, LServer) end,
805 5665 case transaction(HostType, F) of
806 {atomic, ok} ->
807 5665 ok;
808 Result ->
809
:-(
?LOG_ERROR(#{what => remove_user_transaction_failed, reason => Result})
810 end,
811 5665 {ok, Acc1}.
812
813 -spec try_send_unsubscription_to_rosteritems(mongoose_acc:t(), jid:jid()) ->
814 mongoose_acc:t().
815 try_send_unsubscription_to_rosteritems(Acc, JID) ->
816 5665 try
817 5665 send_unsubscription_to_rosteritems(Acc, JID)
818 catch
819 E:R:S ->
820
:-(
?LOG_WARNING(#{what => roster_unsubcribe_failed,
821
:-(
class => E, reason => R, stacktrace => S}),
822
:-(
Acc
823 end.
824
825 %% For each contact with Subscription:
826 %% Both or From, send a "unsubscribed" presence stanza;
827 %% Both or To, send a "unsubscribe" presence stanza.
828 -spec send_unsubscription_to_rosteritems(mongoose_acc:t(), jid:jid()) -> mongoose_acc:t().
829 send_unsubscription_to_rosteritems(Acc, JID) ->
830 5665 #jid{luser = LUser, lserver = LServer} = JID,
831 5665 Acc1 = get_user_roster(Acc, LUser, LServer, mongoose_acc:host_type(Acc)),
832 5665 RosterItems = mongoose_acc:get(roster, items, [], Acc1),
833 5665 lists:foreach(fun(RosterItem) ->
834 215 send_unsubscribing_presence(JID, RosterItem)
835 end, RosterItems),
836 5665 Acc1.
837
838 -spec send_unsubscribing_presence(From :: jid:jid(), Item :: roster()) -> ok | mongoose_acc:t().
839 send_unsubscribing_presence(From, #roster{ subscription = Subscription } = Item) ->
840 239 BareFrom = jid:to_bare(From),
841 239 ContactJID = jid:make_noprep(Item#roster.jid),
842 239 IsTo = Subscription == both orelse Subscription == to,
843 239 IsFrom = Subscription == both orelse Subscription == from,
844 239 case IsTo of
845 87 true -> send_presence_type(BareFrom, ContactJID, <<"unsubscribe">>);
846 152 _ -> ok
847 end,
848 239 case IsFrom of
849 86 true -> send_presence_type(BareFrom, ContactJID, <<"unsubscribed">>);
850 153 _ -> ok
851 end.
852
853 send_presence_type(From, To, Type) ->
854 173 ejabberd_router:route(From, To,
855 #xmlel{name = <<"presence">>,
856 attrs = [{<<"type">>, Type}], children = []}).
857
858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
859
860 -spec remove_domain(Acc, Params, Extra) -> {ok , Acc} when
861 Acc :: mongoose_domain_api:remove_domain_acc(),
862 Params :: #{domain := jid:lserver()},
863 Extra :: gen_hook:extra().
864 remove_domain(Acc, #{domain := Domain}, #{host_type := HostType}) ->
865 18 case mongoose_lib:is_exported(mod_roster_backend, remove_domain_t, 2) of
866 true ->
867 18 F = fun() -> mod_roster_backend:remove_domain_t(HostType, Domain) end,
868 18 case transaction(HostType, F) of
869 {atomic, ok} ->
870 18 ok;
871 Result ->
872
:-(
?LOG_ERROR(#{what => remove_domain_transaction_failed,
873
:-(
reason => Result})
874 end;
875 false ->
876
:-(
ok
877 end,
878 18 {ok, Acc}.
879
880 -spec set_items(mongooseim:host_type(), jid:jid(), exml:element()) -> ok | {error, any()}.
881 set_items(HostType, #jid{luser = LUser, lserver = LServer}, SubEl) ->
882
:-(
F = fun() -> set_items_t(HostType, LUser, LServer, SubEl) end,
883
:-(
case transaction(HostType, F) of
884 {atomic, _} ->
885
:-(
ok;
886 Result ->
887
:-(
{error, Result}
888 end.
889
890 set_items_t(HostType, LUser, LServer, #xmlel{children = Els}) ->
891
:-(
lists:foreach(fun(El) ->
892
:-(
process_item_set_t(HostType, LUser, LServer, El)
893 end, Els).
894
895 %% @doc add a contact to roster, or update
896 -spec set_roster_entry(mongooseim:host_type(), jid:jid(), jid:jid(), binary(), [binary()]) ->
897 ok | {error, any()}.
898 set_roster_entry(HostType, UserJid, ContactJid, Name, Groups) ->
899 120 UpdateF = fun(Item) -> Item#roster{name = Name, groups = Groups} end,
900 120 set_roster_item(HostType, ContactJid, UserJid, UserJid, UpdateF).
901
902 %% @doc remove from roster - in practice it means changing subscription state to 'remove'
903 -spec remove_from_roster(mongooseim:host_type(), UserJid :: jid:jid(), ContactJid :: jid:jid()) ->
904 ok | {error, any()}.
905 remove_from_roster(HostType, UserJid, ContactJid) ->
906 33 UpdateF = fun(Item) -> Item#roster{subscription = remove} end,
907 33 set_roster_item(HostType, ContactJid, UserJid, UserJid, UpdateF).
908
909 process_item_set_t(HostType, LUser, LServer,
910 #xmlel{attrs = Attrs, children = Els}) ->
911
:-(
JID1 = jid:from_binary(xml:get_attr_s(<<"jid">>, Attrs)),
912
:-(
case JID1 of
913
:-(
error -> ok;
914 _ ->
915
:-(
LJID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
916
:-(
Item = #roster{usj = {LUser, LServer, LJID},
917 us = {LUser, LServer}, jid = LJID},
918
:-(
Item1 = process_item_attrs_ws(Item, Attrs),
919
:-(
Item2 = process_item_els(Item1, Els),
920
:-(
case Item2#roster.subscription of
921
:-(
remove -> del_roster_t(HostType, LUser, LServer, LJID);
922
:-(
_ -> update_roster_t(HostType, Item2)
923 end
924 end;
925
:-(
process_item_set_t(_HostType, _LUser, _LServer, _) -> ok.
926
927 process_item_attrs_ws(Item, [{<<"jid">>, Val} | Attrs]) ->
928
:-(
case jid:from_binary(Val) of
929 error ->
930
:-(
process_item_attrs_ws(Item, Attrs);
931 JID1 ->
932
:-(
JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
933
:-(
process_item_attrs_ws(Item#roster{jid = JID}, Attrs)
934 end;
935 process_item_attrs_ws(Item, [{<<"name">>, Val} | Attrs]) ->
936
:-(
process_item_attrs_ws(Item#roster{name = Val}, Attrs);
937 process_item_attrs_ws(Item, [{<<"subscription">>, <<"remove">>} | Attrs]) ->
938
:-(
process_item_attrs_ws(Item#roster{subscription = remove}, Attrs);
939 process_item_attrs_ws(Item, [{<<"subscription">>, <<"none">>} | Attrs]) ->
940
:-(
process_item_attrs_ws(Item#roster{subscription = none}, Attrs);
941 process_item_attrs_ws(Item, [{<<"subscription">>, <<"both">>} | Attrs]) ->
942
:-(
process_item_attrs_ws(Item#roster{subscription = both}, Attrs);
943 process_item_attrs_ws(Item, [{<<"subscription">>, <<"from">>} | Attrs]) ->
944
:-(
process_item_attrs_ws(Item#roster{subscription = from}, Attrs);
945 process_item_attrs_ws(Item, [{<<"subscription">>, <<"to">>} | Attrs]) ->
946
:-(
process_item_attrs_ws(Item#roster{subscription = to}, Attrs);
947 process_item_attrs_ws(Item, [_ | Attrs]) ->
948
:-(
process_item_attrs_ws(Item, Attrs);
949 process_item_attrs_ws(Item, []) ->
950
:-(
Item.
951
952 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
953
954 -spec get_jid_info(Acc, Params, Extra) -> {ok, Acc} when
955 Acc :: {subscription_state(), [binary()]},
956 Params :: #{to := jid:jid(), remote := jid:jid() | jid:simple_jid()},
957 Extra :: gen_hook:extra().
958 get_jid_info(_, #{to := ToJID, remote := JID}, #{host_type := HostType}) ->
959 143 ToRosterEntry = get_roster_entry(HostType, ToJID, JID, full),
960 143 RemoteRosterEntryGetter = fun() -> get_roster_entry(HostType, ToJID, jid:to_bare(jid:to_lower(JID)), full) end,
961 143 NewAcc = determine_subscription_state(ToRosterEntry, RemoteRosterEntryGetter),
962 143 {ok, NewAcc}.
963
964 -spec determine_subscription_state(RosterEntry, RosterEntryGetter) -> SubscriptionState when
965 RosterEntry :: roster() | does_not_exist | error,
966 RosterEntryGetter :: fun(() -> RosterEntry) | undefined,
967 SubscriptionState :: {subscription_state(), [binary()]}.
968
:-(
determine_subscription_state(error, _) -> {none, []};
969 68 determine_subscription_state(does_not_exist, undefined) -> {none, []};
970 91 determine_subscription_state(does_not_exist, Getter) -> determine_subscription_state(Getter(), undefined);
971 75 determine_subscription_state(R, _) -> {R#roster.subscription, R#roster.groups}.
972
973 get_roster_old(HostType, #jid{lserver = LServer} = JID) ->
974 2 get_roster_old(HostType, LServer, JID).
975
976 get_roster_old(HostType, DestServer, JID) ->
977 25 A = mongoose_acc:new(#{ location => ?LOCATION,
978 lserver => DestServer,
979 host_type => HostType,
980 element => undefined }),
981 25 A2 = mongoose_hooks:roster_get(A, JID),
982 25 mongoose_acc:get(roster, items, [], A2).
983
984 -spec item_to_map(roster()) -> map().
985 item_to_map(#roster{} = Roster) ->
986 19 ContactJid = jid:make_noprep(jid:to_bare(Roster#roster.jid)),
987 19 ContactName = Roster#roster.name,
988 19 Subs = Roster#roster.subscription,
989 19 Groups = Roster#roster.groups,
990 19 Ask = Roster#roster.ask,
991 19 #{jid => ContactJid, name => ContactName, subscription => Subs,
992 groups => Groups, ask => Ask}.
993
994 -spec config_metrics(mongooseim:host_type()) -> [{gen_mod:opt_key(), gen_mod:opt_value()}].
995 config_metrics(HostType) ->
996 224 mongoose_module_metrics:opts_for_module(HostType, ?MODULE, [backend]).
997
998 %% Backend API wrappers
999
1000 -spec transaction(mongooseim:host_type(), fun(() -> any())) ->
1001 {aborted, any()} | {atomic, any()} | {error, any()}.
1002 transaction(HostType, F) ->
1003 6827 mod_roster_backend:transaction(HostType, F).
1004
1005 -spec read_roster_version(mongooseim:host_type(), jid:luser(), jid:lserver()) ->
1006 binary() | error.
1007 read_roster_version(HostType, LUser, LServer) ->
1008 5 mod_roster_backend:read_roster_version(HostType, LUser, LServer).
1009
1010 -spec write_roster_version(mongooseim:host_type(), jid:luser(), jid:lserver()) -> version().
1011 write_roster_version(HostType, LUser, LServer) ->
1012 1 write_roster_version(HostType, LUser, LServer, no_transaction).
1013
1014 -spec write_roster_version_t(mongooseim:host_type(), jid:luser(), jid:lserver()) -> version().
1015 write_roster_version_t(HostType, LUser, LServer) ->
1016 2 write_roster_version(HostType, LUser, LServer, in_transaction).
1017
1018 -spec write_roster_version(mongooseim:host_type(), jid:luser(), jid:lserver(),
1019 transaction_state()) -> version().
1020 write_roster_version(HostType, LUser, LServer, TransactionState) ->
1021 3 Ver = mongoose_bin:encode_crypto(term_to_binary(os:timestamp())),
1022 3 mod_roster_backend:write_roster_version(HostType, LUser, LServer, TransactionState, Ver),
1023 3 Ver.
1024
1025 -spec get_roster(mongooseim:host_type(), jid:luser(), jid:lserver()) -> [roster()].
1026 get_roster(HostType, LUser, LServer) ->
1027 5901 mod_roster_backend:get_roster(HostType, LUser, LServer).
1028
1029 -spec get_roster_entry(mongooseim:host_type(), jid:jid(), contact(), entry_format()) ->
1030 roster() | does_not_exist | error.
1031 get_roster_entry(HostType, #jid{luser = LUser, lserver = LServer}, LJid, Format) ->
1032 278 mod_roster_backend:get_roster_entry(HostType, LUser, LServer, LJid, no_transaction, Format).
1033
1034 -spec get_roster_entry_t(mongooseim:host_type(), jid:jid(), contact(), entry_format()) ->
1035 roster() | does_not_exist | error.
1036 get_roster_entry_t(HostType, #jid{luser = LUser, lserver = LServer}, LJid, Format) ->
1037 1144 mod_roster_backend:get_roster_entry(HostType, LUser, LServer, LJid, in_transaction, Format).
1038
1039 -spec roster_subscribe_t(mongooseim:host_type(), roster()) -> ok.
1040 roster_subscribe_t(HostType, Item) ->
1041 937 mod_roster_backend:roster_subscribe_t(HostType, Item).
1042
1043 -spec update_roster_t(mongooseim:host_type(), roster()) -> ok.
1044 update_roster_t(HostType, Item) ->
1045 151 mod_roster_backend:update_roster_t(HostType, Item).
1046
1047 -spec del_roster_t(mongooseim:host_type(), jid:luser(), jid:lserver(), contact()) -> ok.
1048 del_roster_t(HostType, LUser, LServer, LJID) ->
1049 30 mod_roster_backend:del_roster_t(HostType, LUser, LServer, LJID).
Line Hits Source