./ct_report/coverage/mod_mam_mnesia_prefs.COVER.html

1 %%%-------------------------------------------------------------------
2 %%% @author Uvarov Michael <arcusfelis@gmail.com>
3 %%% @copyright (C) 2013, Uvarov Michael
4 %%% @doc A backend for storing MAM preferencies using Mnesia.
5 %%%
6 %%% All preferencies of each user are stored inside a single row.
7 %%% All operations are dirty.
8 %%% @end
9 %%%-------------------------------------------------------------------
10 -module(mod_mam_mnesia_prefs).
11
12 %% ----------------------------------------------------------------------
13 %% Exports
14
15 %% gen_mod handlers
16 -behaviour(gen_mod).
17 -export([start/2, stop/1, supported_features/0]).
18
19 %% MAM hook handlers
20 -behaviour(ejabberd_gen_mam_prefs).
21 -export([get_behaviour/3,
22 get_prefs/3,
23 set_prefs/3,
24 remove_archive/3]).
25
26 -include("mongoose.hrl").
27 -include("jlib.hrl").
28 -include_lib("exml/include/exml.hrl").
29
30 -record(mam_prefs, {host_user :: {jid:server(), jid:user()},
31 default_mode,
32 always_rules :: list(),
33 never_rules :: list()
34 }).
35 -type mam_prefs() :: #mam_prefs{}.
36 -type behaviour() :: 'always' | 'never' | 'roster'.
37
38 %% ----------------------------------------------------------------------
39 %% gen_mod callbacks
40 %% Starting and stopping functions for users' archives
41
42 -spec start(mongooseim:host_type(), gen_mod:module_opts()) -> ok.
43 start(HostType, Opts) ->
44 18 mongoose_mnesia:create_table(mam_prefs,
45 [{disc_copies, [node()]},
46 {attributes, record_info(fields, mam_prefs)}]),
47 18 gen_hook:add_handlers(hooks(HostType, Opts)).
48
49 -spec stop(mongooseim:host_type()) -> ok.
50 stop(HostType) ->
51 18 Opts = gen_mod:get_loaded_module_opts(HostType, ?MODULE),
52 18 gen_hook:delete_handlers(hooks(HostType, Opts)).
53
54 -spec supported_features() -> [atom()].
55 supported_features() ->
56 18 [dynamic_domains].
57
58 %% ----------------------------------------------------------------------
59 %% Hooks
60
61 hooks(HostType, Opts) ->
62 36 lists:flatmap(fun(Type) -> hooks(HostType, Type, Opts) end, [pm, muc]).
63
64 hooks(HostType, pm, #{pm := true}) ->
65 32 [{mam_get_behaviour, HostType, fun ?MODULE:get_behaviour/3, #{}, 50},
66 {mam_get_prefs, HostType, fun ?MODULE:get_prefs/3, #{}, 50},
67 {mam_set_prefs, HostType, fun ?MODULE:set_prefs/3, #{}, 50},
68 {mam_remove_archive, HostType, fun ?MODULE:remove_archive/3, #{}, 50}];
69 hooks(HostType, muc, #{muc := true}) ->
70 8 [{mam_muc_get_behaviour, HostType, fun ?MODULE:get_behaviour/3, #{}, 50},
71 {mam_muc_get_prefs, HostType, fun ?MODULE:get_prefs/3, #{}, 50},
72 {mam_muc_set_prefs, HostType, fun ?MODULE:set_prefs/3, #{}, 50},
73 {mam_muc_remove_archive, HostType, fun ?MODULE:remove_archive/3, #{}, 50}];
74 hooks(_HostType, _Opt, _Opts) ->
75 32 [].
76
77 %% ----------------------------------------------------------------------
78 %% Internal functions and callbacks
79
80 -spec get_behaviour(Acc, Params, Extra) -> {ok, Acc} when
81 Acc :: mod_mam:archive_behaviour(),
82 Params :: ejabberd_gen_mam_prefs:get_behaviour_params(),
83 Extra :: gen_hook:extra().
84 get_behaviour(DefaultBehaviour,
85 #{owner := LocJID, remote := RemJID},
86 _Extra) ->
87 656 get_behaviour2(DefaultBehaviour, LocJID, RemJID);
88 get_behaviour(DefaultBehaviour,
89 #{room := LocJID, remote := RemJID},
90 _Extra) ->
91 140 get_behaviour2(DefaultBehaviour, LocJID, RemJID).
92
93 get_behaviour2(DefaultBehaviour, LocJID, RemJID) ->
94 796 SU = su_key(LocJID),
95 796 case mnesia:dirty_read(mam_prefs, SU) of
96 628 [] -> {ok, DefaultBehaviour};
97 168 [User] -> {ok, get_behaviour3(User, LocJID, RemJID)}
98 end.
99
100
101 -spec get_behaviour3(mam_prefs(), LocJID :: jid:jid(),
102 RemJID :: jid:jid()) -> behaviour().
103 get_behaviour3(#mam_prefs{default_mode = always, never_rules = NeverJIDs}, LocJID, RemJID) ->
104 56 IsNever = match_jid(LocJID, RemJID, NeverJIDs),
105 56 case IsNever of
106 16 true -> never;
107 40 false -> always
108 end;
109 get_behaviour3(#mam_prefs{default_mode = never, always_rules = AlwaysJIDs}, LocJID, RemJID) ->
110 56 IsAlways = match_jid(LocJID, RemJID, AlwaysJIDs),
111 56 case IsAlways of
112 16 true -> always;
113 40 false -> never
114 end;
115 get_behaviour3(#mam_prefs{default_mode = roster,
116 never_rules = NeverJIDs,
117 always_rules = AlwaysJIDs}, LocJID, RemJID) ->
118 56 IsNever = match_jid(LocJID, RemJID, NeverJIDs),
119 56 case IsNever of
120 16 true -> never;
121 false ->
122 40 IsAlways = match_jid(LocJID, RemJID, AlwaysJIDs),
123 40 case IsAlways of
124 16 true -> always;
125 24 false -> roster
126 end
127 end.
128
129 -spec set_prefs(Acc, Params, Extra) -> {ok, Acc} when
130 Acc :: term(),
131 Params :: ejabberd_gen_mam_prefs:set_prefs_params(),
132 Extra :: gen_hook:extra().
133 set_prefs(_Result,
134 #{archive_id := ArcID, owner := ArcJID, default_mode := DefaultMode,
135 always_jids := AlwaysJIDs, never_jids := NeverJIDs},
136 _Extra) ->
137 88 try
138 88 {ok, set_prefs1(ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs)}
139 catch _Type:Error ->
140
:-(
{ok, {error, Error}}
141 end.
142
143 set_prefs1(_ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs) ->
144 88 SU = su_key(ArcJID),
145 88 NewARules = lists:usort(rules(ArcJID, AlwaysJIDs)),
146 88 NewNRules = lists:usort(rules(ArcJID, NeverJIDs)),
147 88 User = #mam_prefs{
148 host_user = SU,
149 default_mode = DefaultMode,
150 always_rules = NewARules,
151 never_rules = NewNRules
152 },
153 88 mnesia:sync_dirty(fun() ->
154 88 mnesia:write(User)
155 end),
156 88 ok.
157
158
159 -spec get_prefs(Acc, Params, Extra) -> {ok, Acc} when
160 Acc :: mod_mam:preference(),
161 Params :: ejabberd_gen_mam_prefs:get_prefs_params(),
162 Extra :: gen_hook:extra().
163 get_prefs({GlobalDefaultMode, _, _}, #{owner := ArcJID}, _Extra) ->
164
165 46 SU = su_key(ArcJID),
166 46 case mnesia:dirty_read(mam_prefs, SU) of
167 [] ->
168
:-(
{ok, {GlobalDefaultMode, [], []}};
169 [#mam_prefs{default_mode=DefaultMode,
170 always_rules=ARules, never_rules=NRules}] ->
171 46 AlwaysJIDs = jids(ArcJID, ARules),
172 46 NeverJIDs = jids(ArcJID, NRules),
173 46 {ok, {DefaultMode, AlwaysJIDs, NeverJIDs}}
174 end.
175
176 -spec remove_archive(Acc, Params, Extra) -> {ok, Acc} when
177 Acc :: term(),
178 Params :: #{archive_id := mod_mam:archive_id() | undefined, owner => jid:jid(), room => jid:jid()},
179 Extra :: gen_hook:extra().
180 remove_archive(Acc, #{owner := ArcJID}, _Extra) ->
181 72 remove_archive(ArcJID),
182 72 {ok, Acc};
183 remove_archive(Acc, #{room := ArcJID}, _Extra) ->
184 120 remove_archive(ArcJID),
185 120 {ok, Acc}.
186
187 remove_archive(ArcJID) ->
188 192 SU = su_key(ArcJID),
189 192 mnesia:sync_dirty(fun() ->
190 192 mnesia:delete(mam_prefs, SU, write)
191 end).
192
193 %% ----------------------------------------------------------------------
194 %% Helpers
195
196 -spec su_key(jid:jid()) -> jid:simple_bare_jid().
197 su_key(#jid{lserver=LocLServer, luser=LocLUser}) ->
198 1122 {LocLServer, LocLUser}.
199
200
201 -spec jids(jid:jid(),
202 [jid:literal_jid() | jid:simple_bare_jid() | jid:simple_jid()]
203 ) -> [jid:literal_jid()].
204 jids(ArcJID, Rules) ->
205 92 [jid:to_binary(rule_to_jid(ArcJID, Rule)) || Rule <- Rules].
206
207
208 -spec rule_to_jid(jid:jid(),
209 jid:luser() | jid:simple_bare_jid() | jid:simple_jid()
210 ) -> jid:simple_jid().
211 rule_to_jid(#jid{lserver=LServer}, RemLUser) when is_binary(RemLUser) ->
212 48 {RemLUser, LServer, <<>>};
213 rule_to_jid(_ArcJID, {RemLServer, RemLUser, RemLResource}) ->
214
:-(
{RemLUser, RemLServer, RemLResource};
215 rule_to_jid(_ArcJID, {RemLServer, RemLUser}) ->
216 8 {RemLUser, RemLServer, <<>>}.
217
218
219 -spec rules(jid:jid(), [jid:literal_jid()]) ->
220 [jid:literal_jid() | jid:simple_bare_jid() | jid:simple_jid()].
221 rules(ArcJID, BinJIDs) ->
222 176 [rule(ArcJID, jid:from_binary(BinJID)) || BinJID <- BinJIDs].
223
224
225 -spec rule(jid:jid(), jid:jid()) ->
226 jid:literal_jid() | jid:simple_bare_jid() | jid:simple_jid().
227 rule(#jid{lserver=LServer}, #jid{lserver=LServer, luser=RemLUser, lresource = <<>>}) ->
228 304 RemLUser;
229 rule(_ArcJID, #jid{lserver=RemLServer, luser=RemLUser, lresource = <<>>}) ->
230 8 {RemLServer, RemLUser};
231 rule(_ArcJID, #jid{lserver=RemLServer, luser=RemLUser, lresource=RemLResource}) ->
232 144 {RemLServer, RemLUser, RemLResource}.
233
234
235 -spec is_bare_jid(jid:jid()) -> boolean().
236
:-(
is_bare_jid(#jid{lresource = <<>>}) -> true;
237 208 is_bare_jid(_) -> false.
238
239
240 -spec match_jid(jid:jid(), jid:jid(), [any()]) -> boolean().
241 match_jid(ArcJID, JID, JIDs) ->
242 208 case is_bare_jid(JID) of
243 true ->
244
:-(
ordsets:is_element(rule(ArcJID, JID), JIDs);
245 false ->
246 208 BareJID = jid:to_bare(JID),
247 208 ordsets:is_element(rule(ArcJID, BareJID), JIDs)
248 orelse
249 144 ordsets:is_element(rule(ArcJID, JID), JIDs)
250 end.
Line Hits Source