./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 -export([start/2, stop/1, supported_features/0]).
17
18 %% MAM hook handlers
19 -behaviour(ejabberd_gen_mam_prefs).
20 -export([get_behaviour/5,
21 get_prefs/4,
22 set_prefs/7,
23 remove_archive/4]).
24
25 -ignore_xref([remove_archive/4, start/2, stop/1, supported_features/0]).
26
27 -include("mongoose.hrl").
28 -include("jlib.hrl").
29 -include_lib("exml/include/exml.hrl").
30
31 -record(mam_prefs, {host_user :: {jid:server(), jid:user()},
32 default_mode,
33 always_rules :: list(),
34 never_rules :: list()
35 }).
36 -type mam_prefs() :: #mam_prefs{}.
37 -type behaviour() :: 'always' | 'never' | 'roster'.
38
39 %% ----------------------------------------------------------------------
40 %% gen_mod callbacks
41 %% Starting and stopping functions for users' archives
42
43 -spec start(Host :: jid:server(), Opts :: list()) -> any().
44 start(Host, Opts) ->
45 19 mnesia:create_table(mam_prefs,
46 [{disc_copies, [node()]},
47 {attributes, record_info(fields, mam_prefs)}]),
48 19 mnesia:add_table_copy(mam_prefs, node(), disc_copies),
49 19 case gen_mod:get_module_opt(Host, ?MODULE, pm, false) of
50 true ->
51 14 start_pm(Host, Opts);
52 false ->
53 5 ok
54 end,
55 19 case gen_mod:get_module_opt(Host, ?MODULE, muc, false) of
56 true ->
57 5 start_muc(Host, Opts);
58 false ->
59 14 ok
60 end.
61
62
63 -spec stop(Host :: jid:server()) -> any().
64 stop(Host) ->
65 19 case gen_mod:get_module_opt(Host, ?MODULE, pm, false) of
66 true ->
67 14 stop_pm(Host);
68 false ->
69 5 ok
70 end,
71 19 case gen_mod:get_module_opt(Host, ?MODULE, muc, false) of
72 true ->
73 5 stop_muc(Host);
74 false ->
75 14 ok
76 end.
77
78 -spec supported_features() -> [atom()].
79 supported_features() ->
80 19 [dynamic_domains].
81
82 %% ----------------------------------------------------------------------
83 %% Add hooks for mod_mam
84
85 -spec start_pm(jid:server(), list()) -> 'ok'.
86 start_pm(Host, _Opts) ->
87 14 ejabberd_hooks:add(mam_get_behaviour, Host, ?MODULE, get_behaviour, 50),
88 14 ejabberd_hooks:add(mam_get_prefs, Host, ?MODULE, get_prefs, 50),
89 14 ejabberd_hooks:add(mam_set_prefs, Host, ?MODULE, set_prefs, 50),
90 14 ejabberd_hooks:add(mam_remove_archive, Host, ?MODULE, remove_archive, 50),
91 14 ok.
92
93
94 -spec stop_pm(jid:server()) -> 'ok'.
95 stop_pm(Host) ->
96 14 ejabberd_hooks:delete(mam_get_behaviour, Host, ?MODULE, get_behaviour, 50),
97 14 ejabberd_hooks:delete(mam_get_prefs, Host, ?MODULE, get_prefs, 50),
98 14 ejabberd_hooks:delete(mam_set_prefs, Host, ?MODULE, set_prefs, 50),
99 14 ejabberd_hooks:delete(mam_remove_archive, Host, ?MODULE, remove_archive, 50),
100 14 ok.
101
102
103 %% ----------------------------------------------------------------------
104 %% Add hooks for mod_mam_muc_muc
105
106 -spec start_muc(jid:server(), list()) -> 'ok'.
107 start_muc(Host, _Opts) ->
108 5 ejabberd_hooks:add(mam_muc_get_behaviour, Host, ?MODULE, get_behaviour, 50),
109 5 ejabberd_hooks:add(mam_muc_get_prefs, Host, ?MODULE, get_prefs, 50),
110 5 ejabberd_hooks:add(mam_muc_set_prefs, Host, ?MODULE, set_prefs, 50),
111 5 ejabberd_hooks:add(mam_muc_remove_archive, Host, ?MODULE, remove_archive, 50),
112 5 ok.
113
114
115 -spec stop_muc(jid:server()) -> 'ok'.
116 stop_muc(Host) ->
117 5 ejabberd_hooks:delete(mam_muc_get_behaviour, Host, ?MODULE, get_behaviour, 50),
118 5 ejabberd_hooks:delete(mam_muc_get_prefs, Host, ?MODULE, get_prefs, 50),
119 5 ejabberd_hooks:delete(mam_muc_set_prefs, Host, ?MODULE, set_prefs, 50),
120 5 ejabberd_hooks:delete(mam_muc_remove_archive, Host, ?MODULE, remove_archive, 50),
121 5 ok.
122
123
124 %% ----------------------------------------------------------------------
125 %% Internal functions and callbacks
126
127 -spec get_behaviour(Default :: behaviour(), Host :: jid:server(),
128 ArcID :: mod_mam:archive_id(), LocJID :: jid:jid(),
129 RemJID :: jid:jid()) -> any().
130 get_behaviour(DefaultBehaviour, _Host,
131 _ArcID,
132 LocJID=#jid{},
133 RemJID=#jid{}) ->
134 739 SU = su_key(LocJID),
135 739 case mnesia:dirty_read(mam_prefs, SU) of
136 571 [] -> DefaultBehaviour;
137 168 [User] -> get_behaviour(User, LocJID, RemJID)
138 end.
139
140
141 -spec get_behaviour(mam_prefs(), LocJID :: jid:jid(),
142 RemJID :: jid:jid()) -> behaviour().
143 get_behaviour(#mam_prefs{default_mode = always, never_rules=NeverJIDs}, LocJID, RemJID) ->
144 56 IsNever = match_jid(LocJID, RemJID, NeverJIDs),
145 56 case IsNever of
146 16 true -> never;
147 40 false -> always
148 end;
149 get_behaviour(#mam_prefs{default_mode = never, always_rules=AlwaysJIDs}, LocJID, RemJID) ->
150 56 IsAlways = match_jid(LocJID, RemJID, AlwaysJIDs),
151 56 case IsAlways of
152 16 true -> always;
153 40 false -> never
154 end;
155 get_behaviour(#mam_prefs{default_mode = roster,
156 never_rules=NeverJIDs, always_rules=AlwaysJIDs}, LocJID, RemJID) ->
157 56 IsNever = match_jid(LocJID, RemJID, NeverJIDs),
158 56 case IsNever of
159 16 true -> never;
160 false ->
161 40 IsAlways = match_jid(LocJID, RemJID, AlwaysJIDs),
162 40 case IsAlways of
163 16 true -> always;
164 24 false -> roster
165 end
166 end.
167
168
169 -spec set_prefs(Result :: any(), Host :: jid:server(),
170 ArcID :: mod_mam:archive_id(), ArcJID :: jid:jid(),
171 DefaultMode :: mod_mam:archive_behaviour(),
172 AlwaysJIDs :: [jid:literal_jid()],
173 NeverJIDs :: [jid:literal_jid()]) -> any().
174 set_prefs(_Result, _Host, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs) ->
175 88 try
176 88 set_prefs1(ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs)
177 catch _Type:Error ->
178
:-(
{error, Error}
179 end.
180
181 set_prefs1(_ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs) ->
182 88 SU = su_key(ArcJID),
183 88 NewARules = lists:usort(rules(ArcJID, AlwaysJIDs)),
184 88 NewNRules = lists:usort(rules(ArcJID, NeverJIDs)),
185 88 User = #mam_prefs{
186 host_user = SU,
187 default_mode = DefaultMode,
188 always_rules = NewARules,
189 never_rules = NewNRules
190 },
191 88 mnesia:sync_dirty(fun() ->
192 88 mnesia:write(User)
193 end),
194 88 ok.
195
196
197 -spec get_prefs(mod_mam:preference(), _Host :: jid:server(),
198 _ArcId :: mod_mam:archive_id(), ArcJID :: jid:jid()
199 ) -> mod_mam:preference().
200 get_prefs({GlobalDefaultMode, _, _}, _Host, _ArcID, ArcJID) ->
201 46 SU = su_key(ArcJID),
202 46 case mnesia:dirty_read(mam_prefs, SU) of
203 [] ->
204
:-(
{GlobalDefaultMode, [], []};
205 [#mam_prefs{default_mode=DefaultMode,
206 always_rules=ARules, never_rules=NRules}] ->
207 46 AlwaysJIDs = jids(ArcJID, ARules),
208 46 NeverJIDs = jids(ArcJID, NRules),
209 46 {DefaultMode, AlwaysJIDs, NeverJIDs}
210 end.
211
212 remove_archive(Acc, _Host, _ArcID, ArcJID) ->
213 156 remove_archive(ArcJID),
214 156 Acc.
215
216 remove_archive(ArcJID) ->
217 156 SU = su_key(ArcJID),
218 156 mnesia:sync_dirty(fun() ->
219 156 mnesia:delete(mam_prefs, SU, write)
220 end).
221
222 %% ----------------------------------------------------------------------
223 %% Helpers
224
225 -spec su_key(jid:jid()) -> jid:simple_bare_jid().
226 su_key(#jid{lserver=LocLServer, luser=LocLUser}) ->
227 1029 {LocLServer, LocLUser}.
228
229
230 -spec jids(jid:jid(),
231 [jid:literal_jid() | jid:simple_bare_jid() | jid:simple_jid()]
232 ) -> [jid:literal_jid()].
233 jids(ArcJID, Rules) ->
234 92 [jid:to_binary(rule_to_jid(ArcJID, Rule)) || Rule <- Rules].
235
236
237 -spec rule_to_jid(jid:jid(),
238 jid:luser() | jid:simple_bare_jid() | jid:simple_jid()
239 ) -> jid:simple_jid().
240 rule_to_jid(#jid{lserver=LServer}, RemLUser) when is_binary(RemLUser) ->
241 48 {RemLUser, LServer, <<>>};
242 rule_to_jid(_ArcJID, {RemLServer, RemLUser, RemLResource}) ->
243
:-(
{RemLUser, RemLServer, RemLResource};
244 rule_to_jid(_ArcJID, {RemLServer, RemLUser}) ->
245 8 {RemLUser, RemLServer, <<>>}.
246
247
248 -spec rules(jid:jid(), [jid:literal_jid()]) ->
249 [jid:literal_jid() | jid:simple_bare_jid() | jid:simple_jid()].
250 rules(ArcJID, BinJIDs) ->
251 176 [rule(ArcJID, jid:from_binary(BinJID)) || BinJID <- BinJIDs].
252
253
254 -spec rule(jid:jid(), jid:jid()) ->
255 jid:literal_jid() | jid:simple_bare_jid() | jid:simple_jid().
256 rule(#jid{lserver=LServer}, #jid{lserver=LServer, luser=RemLUser, lresource = <<>>}) ->
257 304 RemLUser;
258 rule(_ArcJID, #jid{lserver=RemLServer, luser=RemLUser, lresource = <<>>}) ->
259 8 {RemLServer, RemLUser};
260 rule(_ArcJID, #jid{lserver=RemLServer, luser=RemLUser, lresource=RemLResource}) ->
261 144 {RemLServer, RemLUser, RemLResource}.
262
263
264 -spec is_bare_jid(jid:jid()) -> boolean().
265
:-(
is_bare_jid(#jid{lresource = <<>>}) -> true;
266 208 is_bare_jid(_) -> false.
267
268
269 -spec match_jid(jid:jid(), jid:jid(), [any()]) -> boolean().
270 match_jid(ArcJID, JID, JIDs) ->
271 208 case is_bare_jid(JID) of
272 true ->
273
:-(
ordsets:is_element(rule(ArcJID, JID), JIDs);
274 false ->
275 208 BareJID = jid:to_bare(JID),
276 208 ordsets:is_element(rule(ArcJID, BareJID), JIDs)
277 orelse
278 144 ordsets:is_element(rule(ArcJID, JID), JIDs)
279 end.
Line Hits Source