./ct_report/coverage/mod_muc_light_db_mnesia.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : mod_muc_light_db_mnesia.erl
3 %%% Author : Piotr Nosek <piotr.nosek@erlang-solutions.com>
4 %%% Purpose : Mnesia backend for mod_muc_light
5 %%% Created : 8 Sep 2014 by Piotr Nosek <piotr.nosek@erlang-solutions.com>
6 %%%
7 %%% This program is free software; you can redistribute it and/or
8 %%% modify it under the terms of the GNU General Public License as
9 %%% published by the Free Software Foundation; either version 2 of the
10 %%% License, or (at your option) any later version.
11 %%%
12 %%% This program is distributed in the hope that it will be useful,
13 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
14 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 %%% General Public License for more details.
16 %%%
17 %%% You should have received a copy of the GNU General Public License
18 %%% along with this program; if not, write to the Free Software
19 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 %%%
21 %%%----------------------------------------------------------------------
22
23 -module(mod_muc_light_db_mnesia).
24 -author('piotr.nosek@erlang-solutions.com').
25
26 -behaviour(mod_muc_light_db_backend).
27
28 %% API
29 -export([
30 start/2,
31 stop/1,
32 create_room/5,
33 destroy_room/2,
34 room_exists/2,
35 get_user_rooms/3,
36 get_user_rooms_count/2,
37 remove_user/3,
38 remove_domain/3,
39 get_config/2,
40 set_config/4,
41 get_blocking/3,
42 get_blocking/4,
43 set_blocking/4,
44 get_aff_users/2,
45 modify_aff_users/5,
46 get_info/2
47 ]).
48
49 %% Extra API for testing
50 -export([force_clear/0]).
51 -ignore_xref([force_clear/0]).
52
53 -include("mod_muc_light.hrl").
54
55 -record(muc_light_room, {
56 room :: jid:simple_bare_jid(),
57 config :: [{atom(), term()}],
58 aff_users :: aff_users(),
59 version :: binary()
60 }).
61
62 -record(muc_light_user_room, {
63 user :: jid:simple_bare_jid(),
64 room :: jid:simple_bare_jid()
65 }).
66
67 -record(muc_light_blocking, {
68 user :: jid:simple_bare_jid(),
69 item :: {user | room, jid:simple_bare_jid()}
70 }).
71
72 -type muc_light_room() :: #muc_light_room{}.
73 -type muc_light_blocking() :: #muc_light_blocking{}.
74
75 %%====================================================================
76 %% API
77 %%====================================================================
78
79 %% ------------------------ Backend start/stop ------------------------
80
81 -spec start(Host :: jid:server(), any()) -> ok.
82 start(_Host, _) ->
83
:-(
init_tables().
84
85 -spec stop(Host :: jid:server()) -> ok.
86 stop(_Host) ->
87
:-(
ok.
88
89 %% ------------------------ General room management ------------------------
90
91 -spec create_room(mongooseim:host_type(), RoomUS :: jid:simple_bare_jid(), Config :: mod_muc_light_room_config:kv(),
92 AffUsers :: aff_users(), Version :: binary()) ->
93 {ok, FinalRoomUS :: jid:simple_bare_jid()} | {error, exists}.
94 create_room(_HostType, RoomUS, Config, AffUsers, Version) ->
95
:-(
{atomic, Res} = mnesia:transaction(fun create_room_transaction/4,
96 [RoomUS, Config, AffUsers, Version]),
97
:-(
Res.
98
99 -spec destroy_room(HostType :: mongooseim:host_type(),
100 RoomUS :: jid:simple_bare_jid()) ->
101 ok | {error, not_exists}.
102 destroy_room(_HostType, RoomUS) ->
103
:-(
{atomic, Res} = mnesia:transaction(fun destroy_room_transaction/1, [RoomUS]),
104
:-(
Res.
105
106 -spec room_exists(HostType :: mongooseim:host_type(),
107 RoomUS :: jid:simple_bare_jid()) -> boolean().
108 room_exists(_HostType, RoomUS) ->
109
:-(
mnesia:dirty_read(muc_light_room, RoomUS) =/= [].
110
111 -spec get_user_rooms(HostType :: mongooseim:host_type(),
112 UserUS :: jid:simple_bare_jid(),
113 MUCServer :: jid:lserver() | undefined) ->
114 [RoomUS :: jid:simple_bare_jid()].
115 get_user_rooms(_HostType, UserUS, _MUCHost) ->
116
:-(
UsersRooms = mnesia:dirty_read(muc_light_user_room, UserUS),
117
:-(
[ UserRoom#muc_light_user_room.room || UserRoom <- UsersRooms ].
118
119 -spec get_user_rooms_count(HostType :: mongooseim:host_type(),
120 UserUS :: jid:simple_bare_jid()) ->
121 non_neg_integer().
122 get_user_rooms_count(_HostType, UserUS) ->
123
:-(
length(mnesia:dirty_read(muc_light_user_room, UserUS)).
124
125 -spec remove_user(HostType :: mongooseim:host_type(),
126 UserUS :: jid:simple_bare_jid(),
127 Version :: binary()) ->
128 mod_muc_light_db_backend:remove_user_return() | {error, term()}.
129 remove_user(_HostType, UserUS, Version) ->
130
:-(
mnesia:dirty_delete(muc_light_blocking, UserUS),
131
:-(
{atomic, Res} = mnesia:transaction(fun remove_user_transaction/2, [UserUS, Version]),
132
:-(
Res.
133
134 -spec remove_domain(mongooseim:host_type(), jid:lserver(), jid:lserver()) -> ok.
135 remove_domain(_HostType, _RoomS, _LServer) ->
136
:-(
ok.
137
138 %% ------------------------ Configuration manipulation ------------------------
139
140 -spec get_config(HostType :: mongooseim:host_type(),
141 RoomUS :: jid:simple_bare_jid()) ->
142 {ok, mod_muc_light_room_config:kv(), Version :: binary()} | {error, not_exists}.
143 get_config(_HostType, RoomUS) ->
144
:-(
case mnesia:dirty_read(muc_light_room, RoomUS) of
145
:-(
[] -> {error, not_exists};
146
:-(
[#muc_light_room{ config = Config, version = Version }] -> {ok, Config, Version}
147 end.
148
149 -spec set_config(HostType :: mongooseim:host_type(),
150 RoomUS :: jid:simple_bare_jid(),
151 Config :: mod_muc_light_room_config:kv(),
152 Version :: binary()) ->
153 {ok, PrevVersion :: binary()} | {error, not_exists}.
154 set_config(_HostType, RoomUS, ConfigChanges, Version) ->
155
:-(
{atomic, Res} = mnesia:transaction(fun set_config_transaction/3,
156 [RoomUS, ConfigChanges, Version]),
157
:-(
Res.
158
159 %% ------------------------ Blocking manipulation ------------------------
160
161 -spec get_blocking(HostType :: mongooseim:host_type(),
162 UserUS :: jid:simple_bare_jid(), MUCServer :: jid:lserver()) ->
163 [blocking_item()].
164 get_blocking(_HostType, UserUS, _MUCServer) ->
165
:-(
[ {What, deny, Who}
166
:-(
|| #muc_light_blocking{ item = {What, Who} } <- dirty_get_blocking_raw(UserUS) ].
167
168 -spec get_blocking(HostType :: mongooseim:host_type(),
169 UserUS :: jid:simple_bare_jid(),
170 MUCServer :: jid:lserver(),
171 WhatWhos :: [{blocking_what(), jid:simple_bare_jid()}]) ->
172 blocking_action().
173 get_blocking(_HostType, UserUS, _MUCServer, WhatWhos) ->
174
:-(
Blocklist = dirty_get_blocking_raw(UserUS),
175
:-(
case lists:any(
176 fun(WhatWho) ->
177
:-(
lists:keyfind(WhatWho, #muc_light_blocking.item, Blocklist) =/= false
178 end, WhatWhos) of
179
:-(
true -> deny;
180
:-(
false -> allow
181 end.
182
183 -spec set_blocking(HostType :: mongooseim:host_type(),
184 UserUS :: jid:simple_bare_jid(),
185 MUCServer :: jid:lserver(),
186 BlockingItems :: [blocking_item()]) -> ok.
187 set_blocking(_HostType, _UserUS, _MUCServer, []) ->
188
:-(
ok;
189 set_blocking(HostType, UserUS, MUCServer, [{What, deny, Who} | RBlockingItems]) ->
190
:-(
mnesia:dirty_write(#muc_light_blocking{ user = UserUS, item = {What, Who} }),
191
:-(
set_blocking(HostType, UserUS, MUCServer, RBlockingItems);
192 set_blocking(HostType, UserUS, MUCServer, [{What, allow, Who} | RBlockingItems]) ->
193
:-(
mnesia:dirty_delete_object(#muc_light_blocking{ user = UserUS, item = {What, Who} }),
194
:-(
set_blocking(HostType, UserUS, MUCServer, RBlockingItems).
195
196 %% ------------------------ Affiliations manipulation ------------------------
197
198 -spec get_aff_users(HostType :: mongooseim:host_type(),
199 RoomUS :: jid:simple_bare_jid()) ->
200 {ok, aff_users(), Version :: binary()} | {error, not_exists}.
201 get_aff_users(_HostType, RoomUS) ->
202
:-(
case mnesia:dirty_read(muc_light_room, RoomUS) of
203
:-(
[] -> {error, not_exists};
204
:-(
[#muc_light_room{ aff_users = AffUsers, version = Version }] -> {ok, AffUsers, Version}
205 end.
206
207 -spec modify_aff_users(HostType :: mongooseim:host_type(),
208 RoomUS :: jid:simple_bare_jid(),
209 AffUsersChanges :: aff_users(),
210 ExternalCheck :: external_check_fun(),
211 Version :: binary()) ->
212 mod_muc_light_db_backend:modify_aff_users_return().
213 modify_aff_users(_HostType, RoomUS, AffUsersChanges, ExternalCheck, Version) ->
214
:-(
{atomic, Res} = mnesia:transaction(fun modify_aff_users_transaction/4,
215 [RoomUS, AffUsersChanges, ExternalCheck, Version]),
216
:-(
Res.
217
218 %% ------------------------ Misc ------------------------
219
220 -spec get_info(HostType :: mongooseim:host_type(),
221 RoomUS :: jid:simple_bare_jid()) ->
222 {ok, mod_muc_light_room_config:kv(), aff_users(), Version :: binary()}
223 | {error, not_exists}.
224 get_info(_HostType, RoomUS) ->
225
:-(
case mnesia:dirty_read(muc_light_room, RoomUS) of
226 [] ->
227
:-(
{error, not_exists};
228 [#muc_light_room{ config = Config, aff_users = AffUsers, version = Version }] ->
229
:-(
{ok, Config, AffUsers, Version}
230 end.
231
232 %%====================================================================
233 %% API for tests
234 %%====================================================================
235
236 -spec force_clear() -> ok.
237 force_clear() ->
238
:-(
lists:foreach(fun(RoomUS) -> mod_muc_light_utils:run_forget_room_hook(RoomUS) end,
239 mnesia:dirty_all_keys(muc_light_room)),
240
:-(
lists:foreach(fun mnesia:clear_table/1,
241 [muc_light_room, muc_light_user_room, muc_light_blocking]).
242
243 %%====================================================================
244 %% Internal functions
245 %%====================================================================
246
247 %% ------------------------ Schema creation ------------------------
248
249 -spec init_tables() -> ok.
250 init_tables() ->
251
:-(
mongoose_mnesia:create_table(muc_light_room,
252 [{disc_copies, [node()]},
253 {attributes, record_info(fields, muc_light_room)}]),
254
:-(
mongoose_mnesia:create_table(muc_light_user_room,
255 [{disc_copies, [node()]}, {type, bag},
256 {attributes, record_info(fields, muc_light_user_room)}]),
257
:-(
mongoose_mnesia:create_table(muc_light_blocking,
258 [{disc_copies, [node()]}, {type, bag},
259 {attributes, record_info(fields, muc_light_blocking)}]),
260
:-(
ok.
261
262 %% ------------------------ General room management ------------------------
263
264 %% Expects config to have unique fields!
265 -spec create_room_transaction(RoomUS :: jid:simple_bare_jid(),
266 Config :: mod_muc_light_room_config:kv(),
267 AffUsers :: aff_users(),
268 Version :: binary()) ->
269 {ok, FinalRoomUS :: jid:simple_bare_jid()} | {error, exists}.
270 create_room_transaction({<<>>, Domain}, Config, AffUsers, Version) ->
271
:-(
NodeCandidate = mongoose_bin:gen_from_timestamp(),
272
:-(
NewNode = case mnesia:wread({muc_light_room, {NodeCandidate, Domain}}) of
273
:-(
[_] -> <<>>;
274
:-(
[] -> NodeCandidate
275 end,
276
:-(
create_room_transaction({NewNode, Domain}, Config, AffUsers, Version);
277 create_room_transaction(RoomUS, Config, AffUsers, Version) ->
278
:-(
case mnesia:wread({muc_light_room, RoomUS}) of
279 [_] ->
280
:-(
{error, exists};
281 [] ->
282
:-(
RoomRecord = #muc_light_room{
283 room = RoomUS,
284 config = lists:sort(Config),
285 aff_users = AffUsers,
286 version = Version
287 },
288
:-(
ok = mnesia:write(RoomRecord),
289
:-(
lists:foreach(
290 fun({User, _}) ->
291
:-(
UserRoomRecord = #muc_light_user_room{
292 user = User,
293 room = RoomUS
294 },
295
:-(
ok = mnesia:write(UserRoomRecord)
296 end, AffUsers),
297
:-(
{ok, RoomUS}
298 end.
299
300 -spec destroy_room_transaction(RoomUS :: jid:simple_bare_jid()) -> ok | {error, not_exists}.
301 destroy_room_transaction(RoomUS) ->
302
:-(
case mnesia:wread({muc_light_room, RoomUS}) of
303 [] ->
304
:-(
{error, not_exists};
305 [Rec] ->
306
:-(
AffUsers = Rec#muc_light_room.aff_users,
307
:-(
lists:foreach(
308 fun({User, _}) ->
309
:-(
ok = mnesia:delete_object(#muc_light_user_room{user = User, room = RoomUS})
310 end, AffUsers),
311
:-(
mnesia:delete({muc_light_room, RoomUS})
312 end.
313
314 -spec remove_user_transaction(UserUS :: jid:simple_bare_jid(), Version :: binary()) ->
315 mod_muc_light_db_backend:remove_user_return().
316 remove_user_transaction(UserUS, Version) ->
317
:-(
lists:map(
318 fun(#muc_light_user_room{ room = RoomUS }) ->
319
:-(
{RoomUS, modify_aff_users_transaction(
320
:-(
RoomUS, [{UserUS, none}], fun(_, _) -> ok end, Version)}
321 end, mnesia:read(muc_light_user_room, UserUS)).
322
323 %% ------------------------ Configuration manipulation ------------------------
324
325 %% Expects config changes to have unique fields!
326 -spec set_config_transaction(RoomUS :: jid:simple_bare_jid(),
327 ConfigChanges :: mod_muc_light_room_config:kv(),
328 Version :: binary()) ->
329 {ok, PrevVersion :: binary()} | {error, not_exists}.
330 set_config_transaction(RoomUS, ConfigChanges, Version) ->
331
:-(
case mnesia:wread({muc_light_room, RoomUS}) of
332 [] ->
333
:-(
{error, not_exists};
334 [#muc_light_room{ config = Config } = Rec] ->
335
:-(
NewConfig = lists:ukeymerge(1, lists:sort(ConfigChanges), Config),
336
:-(
mnesia:write(Rec#muc_light_room{ config = NewConfig, version = Version }),
337
:-(
{ok, Rec#muc_light_room.version}
338 end.
339
340 %% ------------------------ Blocking manipulation ------------------------
341
342 -spec dirty_get_blocking_raw(UserUS :: jid:simple_bare_jid()) -> [muc_light_blocking()].
343 dirty_get_blocking_raw(UserUS) ->
344
:-(
mnesia:dirty_read(muc_light_blocking, UserUS).
345
346 %% ------------------------ Affiliations manipulation ------------------------
347
348 -spec modify_aff_users_transaction(RoomUS :: jid:simple_bare_jid(),
349 AffUsersChanges :: aff_users(),
350 ExternalCheck :: external_check_fun(),
351 Version :: binary()) ->
352 mod_muc_light_db_backend:modify_aff_users_return().
353 modify_aff_users_transaction(RoomUS, AffUsersChanges, ExternalCheck, Version) ->
354
:-(
case mnesia:wread({muc_light_room, RoomUS}) of
355 [] ->
356
:-(
{error, not_exists};
357 [#muc_light_room{ aff_users = AffUsers } = RoomRec] ->
358
:-(
case mod_muc_light_utils:change_aff_users(AffUsers, AffUsersChanges) of
359 {ok, NewAffUsers, _, _, _} = ChangeResult ->
360
:-(
verify_externally_and_submit(
361 RoomUS, RoomRec, ChangeResult, ExternalCheck(RoomUS, NewAffUsers), Version);
362 Error ->
363
:-(
Error
364 end
365 end.
366
367 -spec verify_externally_and_submit(RoomUS :: jid:simple_bare_jid(),
368 RoomRec :: muc_light_room(),
369 ChangeResult :: mod_muc_light_utils:change_aff_success(),
370 CheckResult :: ok | {error, any()},
371 Version :: binary()) ->
372 mod_muc_light_db_backend:modify_aff_users_return().
373 verify_externally_and_submit(
374 RoomUS, #muc_light_room{ aff_users = OldAffUsers, version = PrevVersion } = RoomRec,
375 {ok, NewAffUsers, AffUsersChanged, JoiningUsers, LeavingUsers}, ok, Version) ->
376
:-(
ok = mnesia:write(RoomRec#muc_light_room{ aff_users = NewAffUsers, version = Version }),
377
:-(
update_users_rooms(RoomUS, JoiningUsers, LeavingUsers),
378
:-(
{ok, OldAffUsers, NewAffUsers, AffUsersChanged, PrevVersion};
379 verify_externally_and_submit(_, _, _, Error, _) ->
380
:-(
Error.
381
382 -spec update_users_rooms(RoomUS :: jid:simple_bare_jid(),
383 JoiningUsers :: [jid:simple_bare_jid()],
384 LeavingUsers :: [jid:simple_bare_jid()]) -> ok.
385 update_users_rooms(RoomUS, [User | RJoiningUsers], LeavingUsers) ->
386
:-(
ok = mnesia:write(#muc_light_user_room{ user = User, room = RoomUS }),
387
:-(
update_users_rooms(RoomUS, RJoiningUsers, LeavingUsers);
388 update_users_rooms(RoomUS, [], [User | RLeavingUsers]) ->
389
:-(
ok = mnesia:delete_object(#muc_light_user_room{ user = User, room = RoomUS }),
390
:-(
update_users_rooms(RoomUS, [], RLeavingUsers);
391 update_users_rooms(_RoomUS, [], []) ->
392
:-(
ok.
393
Line Hits Source