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. |