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 |
:-( |
mnesia:create_table(mam_prefs, |
46 |
|
[{disc_copies, [node()]}, |
47 |
|
{attributes, record_info(fields, mam_prefs)}]), |
48 |
:-( |
mnesia:add_table_copy(mam_prefs, node(), disc_copies), |
49 |
:-( |
case gen_mod:get_module_opt(Host, ?MODULE, pm, false) of |
50 |
|
true -> |
51 |
:-( |
start_pm(Host, Opts); |
52 |
|
false -> |
53 |
:-( |
ok |
54 |
|
end, |
55 |
:-( |
case gen_mod:get_module_opt(Host, ?MODULE, muc, false) of |
56 |
|
true -> |
57 |
:-( |
start_muc(Host, Opts); |
58 |
|
false -> |
59 |
:-( |
ok |
60 |
|
end. |
61 |
|
|
62 |
|
|
63 |
|
-spec stop(Host :: jid:server()) -> any(). |
64 |
|
stop(Host) -> |
65 |
:-( |
case gen_mod:get_module_opt(Host, ?MODULE, pm, false) of |
66 |
|
true -> |
67 |
:-( |
stop_pm(Host); |
68 |
|
false -> |
69 |
:-( |
ok |
70 |
|
end, |
71 |
:-( |
case gen_mod:get_module_opt(Host, ?MODULE, muc, false) of |
72 |
|
true -> |
73 |
:-( |
stop_muc(Host); |
74 |
|
false -> |
75 |
:-( |
ok |
76 |
|
end. |
77 |
|
|
78 |
|
-spec supported_features() -> [atom()]. |
79 |
|
supported_features() -> |
80 |
:-( |
[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 |
:-( |
ejabberd_hooks:add(mam_get_behaviour, Host, ?MODULE, get_behaviour, 50), |
88 |
:-( |
ejabberd_hooks:add(mam_get_prefs, Host, ?MODULE, get_prefs, 50), |
89 |
:-( |
ejabberd_hooks:add(mam_set_prefs, Host, ?MODULE, set_prefs, 50), |
90 |
:-( |
ejabberd_hooks:add(mam_remove_archive, Host, ?MODULE, remove_archive, 50), |
91 |
:-( |
ok. |
92 |
|
|
93 |
|
|
94 |
|
-spec stop_pm(jid:server()) -> 'ok'. |
95 |
|
stop_pm(Host) -> |
96 |
:-( |
ejabberd_hooks:delete(mam_get_behaviour, Host, ?MODULE, get_behaviour, 50), |
97 |
:-( |
ejabberd_hooks:delete(mam_get_prefs, Host, ?MODULE, get_prefs, 50), |
98 |
:-( |
ejabberd_hooks:delete(mam_set_prefs, Host, ?MODULE, set_prefs, 50), |
99 |
:-( |
ejabberd_hooks:delete(mam_remove_archive, Host, ?MODULE, remove_archive, 50), |
100 |
:-( |
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 |
:-( |
ejabberd_hooks:add(mam_muc_get_behaviour, Host, ?MODULE, get_behaviour, 50), |
109 |
:-( |
ejabberd_hooks:add(mam_muc_get_prefs, Host, ?MODULE, get_prefs, 50), |
110 |
:-( |
ejabberd_hooks:add(mam_muc_set_prefs, Host, ?MODULE, set_prefs, 50), |
111 |
:-( |
ejabberd_hooks:add(mam_muc_remove_archive, Host, ?MODULE, remove_archive, 50), |
112 |
:-( |
ok. |
113 |
|
|
114 |
|
|
115 |
|
-spec stop_muc(jid:server()) -> 'ok'. |
116 |
|
stop_muc(Host) -> |
117 |
:-( |
ejabberd_hooks:delete(mam_muc_get_behaviour, Host, ?MODULE, get_behaviour, 50), |
118 |
:-( |
ejabberd_hooks:delete(mam_muc_get_prefs, Host, ?MODULE, get_prefs, 50), |
119 |
:-( |
ejabberd_hooks:delete(mam_muc_set_prefs, Host, ?MODULE, set_prefs, 50), |
120 |
:-( |
ejabberd_hooks:delete(mam_muc_remove_archive, Host, ?MODULE, remove_archive, 50), |
121 |
:-( |
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 |
:-( |
SU = su_key(LocJID), |
135 |
:-( |
case mnesia:dirty_read(mam_prefs, SU) of |
136 |
:-( |
[] -> DefaultBehaviour; |
137 |
:-( |
[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 |
:-( |
IsNever = match_jid(LocJID, RemJID, NeverJIDs), |
145 |
:-( |
case IsNever of |
146 |
:-( |
true -> never; |
147 |
:-( |
false -> always |
148 |
|
end; |
149 |
|
get_behaviour(#mam_prefs{default_mode = never, always_rules=AlwaysJIDs}, LocJID, RemJID) -> |
150 |
:-( |
IsAlways = match_jid(LocJID, RemJID, AlwaysJIDs), |
151 |
:-( |
case IsAlways of |
152 |
:-( |
true -> always; |
153 |
:-( |
false -> never |
154 |
|
end; |
155 |
|
get_behaviour(#mam_prefs{default_mode = roster, |
156 |
|
never_rules=NeverJIDs, always_rules=AlwaysJIDs}, LocJID, RemJID) -> |
157 |
:-( |
IsNever = match_jid(LocJID, RemJID, NeverJIDs), |
158 |
:-( |
case IsNever of |
159 |
:-( |
true -> never; |
160 |
|
false -> |
161 |
:-( |
IsAlways = match_jid(LocJID, RemJID, AlwaysJIDs), |
162 |
:-( |
case IsAlways of |
163 |
:-( |
true -> always; |
164 |
:-( |
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 |
:-( |
try |
176 |
:-( |
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 |
:-( |
SU = su_key(ArcJID), |
183 |
:-( |
NewARules = lists:usort(rules(ArcJID, AlwaysJIDs)), |
184 |
:-( |
NewNRules = lists:usort(rules(ArcJID, NeverJIDs)), |
185 |
:-( |
User = #mam_prefs{ |
186 |
|
host_user = SU, |
187 |
|
default_mode = DefaultMode, |
188 |
|
always_rules = NewARules, |
189 |
|
never_rules = NewNRules |
190 |
|
}, |
191 |
:-( |
mnesia:sync_dirty(fun() -> |
192 |
:-( |
mnesia:write(User) |
193 |
|
end), |
194 |
:-( |
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 |
:-( |
SU = su_key(ArcJID), |
202 |
:-( |
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 |
:-( |
AlwaysJIDs = jids(ArcJID, ARules), |
208 |
:-( |
NeverJIDs = jids(ArcJID, NRules), |
209 |
:-( |
{DefaultMode, AlwaysJIDs, NeverJIDs} |
210 |
|
end. |
211 |
|
|
212 |
|
remove_archive(Acc, _Host, _ArcID, ArcJID) -> |
213 |
:-( |
remove_archive(ArcJID), |
214 |
:-( |
Acc. |
215 |
|
|
216 |
|
remove_archive(ArcJID) -> |
217 |
:-( |
SU = su_key(ArcJID), |
218 |
:-( |
mnesia:sync_dirty(fun() -> |
219 |
:-( |
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 |
:-( |
{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 |
:-( |
[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 |
:-( |
{RemLUser, LServer, <<>>}; |
242 |
|
rule_to_jid(_ArcJID, {RemLServer, RemLUser, RemLResource}) -> |
243 |
:-( |
{RemLUser, RemLServer, RemLResource}; |
244 |
|
rule_to_jid(_ArcJID, {RemLServer, RemLUser}) -> |
245 |
:-( |
{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 |
:-( |
[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 |
:-( |
RemLUser; |
258 |
|
rule(_ArcJID, #jid{lserver=RemLServer, luser=RemLUser, lresource = <<>>}) -> |
259 |
:-( |
{RemLServer, RemLUser}; |
260 |
|
rule(_ArcJID, #jid{lserver=RemLServer, luser=RemLUser, lresource=RemLResource}) -> |
261 |
:-( |
{RemLServer, RemLUser, RemLResource}. |
262 |
|
|
263 |
|
|
264 |
|
-spec is_bare_jid(jid:jid()) -> boolean(). |
265 |
:-( |
is_bare_jid(#jid{lresource = <<>>}) -> true; |
266 |
:-( |
is_bare_jid(_) -> false. |
267 |
|
|
268 |
|
|
269 |
|
-spec match_jid(jid:jid(), jid:jid(), [any()]) -> boolean(). |
270 |
|
match_jid(ArcJID, JID, JIDs) -> |
271 |
:-( |
case is_bare_jid(JID) of |
272 |
|
true -> |
273 |
:-( |
ordsets:is_element(rule(ArcJID, JID), JIDs); |
274 |
|
false -> |
275 |
:-( |
BareJID = jid:to_bare(JID), |
276 |
:-( |
ordsets:is_element(rule(ArcJID, BareJID), JIDs) |
277 |
|
orelse |
278 |
:-( |
ordsets:is_element(rule(ArcJID, JID), JIDs) |
279 |
|
end. |