./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 26 mongoose_mnesia:create_table(mam_prefs,
45 [{disc_copies, [node()]},
46 {attributes, record_info(fields, mam_prefs)}]),
47 26 gen_hook:add_handlers(hooks(HostType, Opts)).
48
49 -spec stop(mongooseim:host_type()) -> ok.
50 stop(HostType) ->
51 26 Opts = gen_mod:get_loaded_module_opts(HostType, ?MODULE),
52 26 gen_hook:delete_handlers(hooks(HostType, Opts)).
53
54 -spec supported_features() -> [atom()].
55 supported_features() ->
56
:-(
[dynamic_domains].
57
58 %% ----------------------------------------------------------------------
59 %% Hooks
60
61 hooks(HostType, Opts) ->
62 52 lists:flatmap(fun(Type) -> hooks(HostType, Type, Opts) end, [pm, muc]).
63
64 hooks(HostType, pm, #{pm := true}) ->
65 40 [{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 24 [{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 40 [].
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 704 get_behaviour2(DefaultBehaviour, LocJID, RemJID);
88 get_behaviour(DefaultBehaviour,
89 #{room := LocJID, remote := RemJID},
90 _Extra) ->
91 312 get_behaviour2(DefaultBehaviour, LocJID, RemJID).
92
93 get_behaviour2(DefaultBehaviour, LocJID, RemJID) ->
94 1016 SU = su_key(LocJID),
95 1016 case mnesia:dirty_read(mam_prefs, SU) of
96 680 [] -> {ok, DefaultBehaviour};
97 336 [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 112 IsNever = match_jid(LocJID, RemJID, NeverJIDs),
105 112 case IsNever of
106 24 true -> never;
107 88 false -> always
108 end;
109 get_behaviour3(#mam_prefs{default_mode = never, always_rules = AlwaysJIDs}, LocJID, RemJID) ->
110 112 IsAlways = match_jid(LocJID, RemJID, AlwaysJIDs),
111 112 case IsAlways of
112 24 true -> always;
113 88 false -> never
114 end;
115 get_behaviour3(#mam_prefs{default_mode = roster,
116 never_rules = NeverJIDs,
117 always_rules = AlwaysJIDs}, LocJID, RemJID) ->
118 112 IsNever = match_jid(LocJID, RemJID, NeverJIDs),
119 112 case IsNever of
120 24 true -> never;
121 false ->
122 88 IsAlways = match_jid(LocJID, RemJID, AlwaysJIDs),
123 88 case IsAlways of
124 24 true -> always;
125 64 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 set_prefs1(ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs);
138 set_prefs(_Result,
139 #{archive_id := ArcID, room := ArcJID, default_mode := DefaultMode,
140 always_jids := AlwaysJIDs, never_jids := NeverJIDs},
141 _Extra) ->
142 46 set_prefs1(ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs).
143
144 set_prefs1(ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs) ->
145 134 try
146 134 {ok, set_prefs2(ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs)}
147 catch _Type:Error ->
148
:-(
{ok, {error, Error}}
149 end.
150
151 set_prefs2(_ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs) ->
152 134 SU = su_key(ArcJID),
153 134 NewARules = lists:usort(rules(ArcJID, AlwaysJIDs)),
154 134 NewNRules = lists:usort(rules(ArcJID, NeverJIDs)),
155 134 User = #mam_prefs{
156 host_user = SU,
157 default_mode = DefaultMode,
158 always_rules = NewARules,
159 never_rules = NewNRules
160 },
161 134 mnesia:sync_dirty(fun() ->
162 134 mnesia:write(User)
163 end),
164 134 ok.
165
166
167 -spec get_prefs(Acc, Params, Extra) -> {ok, Acc} when
168 Acc :: mod_mam:preference(),
169 Params :: ejabberd_gen_mam_prefs:get_prefs_params(),
170 Extra :: gen_hook:extra().
171 get_prefs({GlobalDefaultMode, _, _}, #{owner := ArcJID}, _Extra) ->
172 46 get_prefs1(GlobalDefaultMode, ArcJID);
173 get_prefs({GlobalDefaultMode, _, _}, #{room := ArcJID}, _Extra) ->
174 4 get_prefs1(GlobalDefaultMode, ArcJID).
175
176 get_prefs1(GlobalDefaultMode, ArcJID) ->
177 50 SU = su_key(ArcJID),
178 50 case mnesia:dirty_read(mam_prefs, SU) of
179 [] ->
180
:-(
{ok, {GlobalDefaultMode, [], []}};
181 [#mam_prefs{default_mode=DefaultMode,
182 always_rules=ARules, never_rules=NRules}] ->
183 50 AlwaysJIDs = jids(ArcJID, ARules),
184 50 NeverJIDs = jids(ArcJID, NRules),
185 50 {ok, {DefaultMode, AlwaysJIDs, NeverJIDs}}
186 end.
187
188 -spec remove_archive(Acc, Params, Extra) -> {ok, Acc} when
189 Acc :: term(),
190 Params :: #{archive_id := mod_mam:archive_id() | undefined, owner => jid:jid(), room => jid:jid()},
191 Extra :: gen_hook:extra().
192 remove_archive(Acc, #{owner := ArcJID}, _Extra) ->
193 70 remove_archive(ArcJID),
194 70 {ok, Acc};
195 remove_archive(Acc, #{room := ArcJID}, _Extra) ->
196 172 remove_archive(ArcJID),
197 172 {ok, Acc}.
198
199 remove_archive(ArcJID) ->
200 242 SU = su_key(ArcJID),
201 242 mnesia:sync_dirty(fun() ->
202 242 mnesia:delete(mam_prefs, SU, write)
203 end).
204
205 %% ----------------------------------------------------------------------
206 %% Helpers
207
208 -spec su_key(jid:jid()) -> jid:simple_bare_jid().
209 su_key(#jid{lserver=LocLServer, luser=LocLUser}) ->
210 1442 {LocLServer, LocLUser}.
211
212
213 -spec jids(jid:jid(),
214 [jid:literal_jid() | jid:simple_bare_jid() | jid:simple_jid()]
215 ) -> [jid:literal_jid()].
216 jids(ArcJID, Rules) ->
217 100 [jid:to_binary(rule_to_jid(ArcJID, Rule)) || Rule <- Rules].
218
219
220 -spec rule_to_jid(jid:jid(),
221 jid:luser() | jid:simple_bare_jid() | jid:simple_jid()
222 ) -> jid:simple_jid().
223 rule_to_jid(#jid{lserver=LServer}, RemLUser) when is_binary(RemLUser) ->
224 48 {RemLUser, LServer, <<>>};
225 rule_to_jid(_ArcJID, {RemLServer, RemLUser, RemLResource}) ->
226
:-(
{RemLUser, RemLServer, RemLResource};
227 rule_to_jid(_ArcJID, {RemLServer, RemLUser}) ->
228 16 {RemLUser, RemLServer, <<>>}.
229
230
231 -spec rules(jid:jid(), [jid:literal_jid()]) ->
232 [jid:literal_jid() | jid:simple_bare_jid() | jid:simple_jid()].
233 rules(ArcJID, BinJIDs) ->
234 268 [rule(ArcJID, jid:from_binary(BinJID)) || BinJID <- BinJIDs].
235
236
237 -spec rule(jid:jid(), jid:jid()) ->
238 jid:literal_jid() | jid:simple_bare_jid() | jid:simple_jid().
239 rule(#jid{lserver=LServer}, #jid{lserver=LServer, luser=RemLUser, lresource = <<>>}) ->
240 520 RemLUser;
241 rule(_ArcJID, #jid{lserver=RemLServer, luser=RemLUser, lresource = <<>>}) ->
242 16 {RemLServer, RemLUser};
243 rule(_ArcJID, #jid{lserver=RemLServer, luser=RemLUser, lresource=RemLResource}) ->
244 408 {RemLServer, RemLUser, RemLResource}.
245
246
247 -spec is_bare_jid(jid:jid()) -> boolean().
248
:-(
is_bare_jid(#jid{lresource = <<>>}) -> true;
249 424 is_bare_jid(_) -> false.
250
251
252 -spec match_jid(jid:jid(), jid:jid(), [any()]) -> boolean().
253 match_jid(ArcJID, JID, JIDs) ->
254 424 case is_bare_jid(JID) of
255 true ->
256
:-(
ordsets:is_element(rule(ArcJID, JID), JIDs);
257 false ->
258 424 BareJID = jid:to_bare(JID),
259 424 ordsets:is_element(rule(ArcJID, BareJID), JIDs)
260 orelse
261 360 ordsets:is_element(rule(ArcJID, JID), JIDs)
262 end.
Line Hits Source