./ct_report/coverage/mod_muc_rdbms.COVER.html

1 -module(mod_muc_rdbms).
2 -include("mod_muc.hrl").
3 -include("mongoose_logger.hrl").
4
5 -export([init/2,
6 store_room/4,
7 restore_room/3,
8 forget_room/3,
9 get_rooms/2,
10 can_use_nick/4,
11 get_nick/3,
12 set_nick/4,
13 unset_nick/3,
14 remove_domain/3
15 ]).
16
17 -ignore_xref([can_use_nick/4, forget_room/3, get_nick/3, get_rooms/2, remove_domain/3, init/2,
18 restore_room/3, set_nick/4, store_room/4, unset_nick/3]).
19
20 -import(mongoose_rdbms, [prepare/4, execute_successfully/3]).
21
22 %% Host of MUC service
23 -type muc_host() :: jid:server().
24
25 %% User's JID. Can be on another domain accessible over FED.
26 %% Only bare part (user@host) is important.
27 -type client_jid() :: jid:jid().
28 -type room_id() :: pos_integer().
29 -type room_opts() :: [{OptionName :: atom(), OptionValue :: term()}].
30 -type aff() :: atom().
31
32
33 -spec init(mongooseim:host_type(), ModuleOpts :: list()) -> ok.
34 init(HostType, _Opts) ->
35
:-(
prepare_queries(HostType),
36
:-(
ok.
37
38 prepare_queries(HostType) ->
39 %% Queries to muc_rooms table
40
:-(
prepare(muc_insert_room, muc_rooms,
41 [muc_host, room_name, options],
42 <<"INSERT INTO muc_rooms (muc_host, room_name, options)"
43 " VALUES (?, ?, ?)">>),
44
:-(
prepare(muc_select_room_id, muc_rooms,
45 [muc_host, room_name],
46 <<"SELECT id FROM muc_rooms "
47 "WHERE muc_host = ? AND room_name = ?">>),
48
:-(
prepare(muc_select_room, muc_rooms,
49 [muc_host, room_name],
50 <<"SELECT id, options FROM muc_rooms "
51 "WHERE muc_host = ? AND room_name = ?">>),
52
:-(
prepare(muc_delete_room, muc_rooms,
53 [muc_host, room_name],
54 <<"DELETE FROM muc_rooms WHERE muc_host = ? AND room_name = ?">>),
55
:-(
prepare(muc_rooms_remove_domain, muc_rooms,
56 [muc_host],
57 <<"DELETE FROM muc_rooms WHERE muc_host = ?">>),
58
:-(
prepare(muc_select_rooms, muc_rooms, [muc_host],
59 <<"SELECT id, room_name, options FROM muc_rooms WHERE muc_host = ?">>),
60 %% Queries to muc_room_aff table
61
:-(
prepare(muc_insert_aff, muc_room_aff,
62 [room_id, luser, lserver, resource, aff],
63 <<"INSERT INTO muc_room_aff"
64 " (room_id, luser, lserver, resource, aff)"
65 " VALUES(?, ?, ?, ?, ?)">>),
66
:-(
prepare(muc_select_aff, muc_room_aff,
67 [room_id],
68 <<"SELECT luser, lserver, resource, aff "
69 "FROM muc_room_aff WHERE room_id = ?">>),
70
:-(
prepare(muc_delete_aff, muc_room_aff, [room_id],
71 <<"DELETE FROM muc_room_aff WHERE room_id = ?">>),
72
:-(
prepare(muc_room_aff_remove_room_domain, muc_room_aff,
73 ['muc_rooms.muc_host'],
74 <<"DELETE FROM muc_room_aff WHERE room_id IN "
75 "(SELECT id FROM muc_rooms WHERE muc_host = ?)">>),
76
:-(
prepare(muc_room_aff_remove_user_domain, muc_room_aff,
77 [lserver],
78 <<"DELETE FROM muc_room_aff WHERE lserver = ?">>),
79 %% Queries to muc_registered table
80
:-(
prepare(muc_select_nick_user, muc_registered,
81 [muc_host, lserver, nick],
82 <<"SELECT luser FROM muc_registered WHERE muc_host = ?"
83 " AND lserver = ? AND nick = ?">>),
84
:-(
prepare(muc_select_nick, muc_registered,
85 [muc_host, lserver, luser],
86 <<"SELECT nick FROM muc_registered WHERE muc_host = ?"
87 " AND lserver = ? AND luser = ?">>),
88
:-(
prepare(muc_delete_nick, muc_registered,
89 [muc_host, lserver, luser],
90 <<"DELETE FROM muc_registered WHERE muc_host = ?"
91 " AND lserver = ? AND luser = ?">>),
92
:-(
prepare(muc_registered_remove_room_domain, muc_registered,
93 [muc_host],
94 <<"DELETE FROM muc_registered WHERE muc_host = ?">>),
95
:-(
prepare(muc_registered_remove_user_domain, muc_registered,
96 [lserver],
97 <<"DELETE FROM muc_room_aff WHERE lserver = ?">>),
98
:-(
rdbms_queries:prepare_upsert(HostType, muc_nick_upsert, muc_registered,
99 [<<"muc_host">>, <<"luser">>, <<"lserver">>, <<"nick">>],
100 [<<"nick">>],
101 [<<"muc_host">>, <<"luser">>, <<"lserver">>]),
102
:-(
ok.
103
104 %% Room API functions
105
106 -spec remove_domain(mongooseim:host_type(), muc_host(), jid:lserver()) -> ok.
107 remove_domain(HostType, MucHost, Domain) ->
108
:-(
F = fun() ->
109
:-(
mongoose_rdbms:execute_successfully(
110 HostType, muc_registered_remove_room_domain, [MucHost]),
111
:-(
mongoose_rdbms:execute_successfully(
112 HostType, muc_registered_remove_user_domain, [Domain]),
113
:-(
mongoose_rdbms:execute_successfully(
114 HostType, muc_room_aff_remove_room_domain, [MucHost]),
115
:-(
mongoose_rdbms:execute_successfully(
116 HostType, muc_room_aff_remove_user_domain, [Domain]),
117
:-(
mongoose_rdbms:execute_successfully(
118 HostType, muc_rooms_remove_domain, [MucHost]),
119
:-(
ok
120 end,
121
:-(
{atomic, ok} = mongoose_rdbms:sql_transaction(HostType, F),
122
:-(
ok.
123
124 -spec store_room(mongooseim:host_type(), muc_host(), mod_muc:room(), room_opts()) ->
125 ok | {error, term()}.
126 store_room(HostType, MucHost, RoomName, Opts) ->
127
:-(
Affs = proplists:get_value(affiliations, Opts),
128
:-(
NewOpts = proplists:delete(affiliations, Opts),
129
:-(
ExtOpts = jiffy:encode({NewOpts}),
130
:-(
F = fun() ->
131
:-(
forget_room_transaction(HostType, MucHost, RoomName),
132
:-(
store_room_transaction(HostType, MucHost, RoomName, ExtOpts, Affs)
133 end,
134
:-(
{atomic, Res} = mongoose_rdbms:sql_transaction(HostType, F),
135
:-(
Res.
136
137 -spec restore_room(mongooseim:host_type(), muc_host(), mod_muc:room()) ->
138 {ok, room_opts()} | {error, room_not_found} | {error, term()}.
139 restore_room(HostType, MucHost, RoomName) ->
140
:-(
case execute_select_room(HostType, MucHost, RoomName) of
141 {selected, [{ExtRoomID, ExtOpts}]} ->
142
:-(
RoomID = mongoose_rdbms:result_to_integer(ExtRoomID),
143
:-(
FullOpts = get_full_options(HostType, ExtOpts, RoomID),
144
:-(
{ok, FullOpts};
145 {selected, []} ->
146
:-(
{error, room_not_found}
147 end.
148
149 -spec forget_room(mongooseim:host_type(), muc_host(), mod_muc:room()) ->
150 ok | {error, term()}.
151 forget_room(HostType, MucHost, RoomName) ->
152
:-(
F = fun() -> forget_room_transaction(HostType, MucHost, RoomName) end,
153
:-(
{atomic, _Res} = mongoose_rdbms:sql_transaction(HostType, F),
154
:-(
ok.
155
156 %% Room helper functions
157
158 -spec get_rooms(mongooseim:host_type(), muc_host()) -> {ok, [#muc_room{}]}.
159 get_rooms(HostType, MucHost) ->
160
:-(
{selected, RoomRows} = execute_select_rooms(HostType, MucHost),
161
:-(
RoomRecs = [handle_room_row(HostType, MucHost, Row) || Row <- RoomRows],
162
:-(
{ok, RoomRecs}.
163
164 handle_room_row(HostType, MucHost, {ExtRoomID, RoomName, ExtOpts}) ->
165
:-(
RoomID = mongoose_rdbms:result_to_integer(ExtRoomID),
166
:-(
FullOpts = get_full_options(HostType, ExtOpts, RoomID),
167
:-(
#muc_room{name_host = {RoomName, MucHost}, opts = FullOpts}.
168
169 get_full_options(HostType, ExtOpts, RoomID) ->
170
:-(
{selected, Affs} = execute_select_aff(HostType, RoomID),
171
:-(
decode_opts(ExtOpts, Affs).
172
173 %% Nick API functions
174
175 -spec can_use_nick(mongooseim:host_type(), muc_host(), client_jid(), mod_muc:nick()) -> boolean().
176 can_use_nick(HostType, MucHost, Jid, Nick) ->
177
:-(
{UserU, UserS} = jid:to_lus(Jid),
178
:-(
case execute_select_nick_user(HostType, MucHost, UserS, Nick) of
179
:-(
{selected, []} -> true;
180
:-(
{selected, [{U}]} -> U == UserU
181 end.
182
183 %% Get nick associated with jid client_jid() across muc_host() domain
184 -spec get_nick(mongooseim:host_type(), muc_host(), client_jid()) ->
185 {ok, mod_muc:nick()} | {error, not_registered}.
186 get_nick(HostType, MucHost, Jid) ->
187
:-(
{UserU, UserS} = jid:to_lus(Jid),
188
:-(
case execute_select_nick(HostType, MucHost, UserU, UserS) of
189
:-(
{selected, []} -> {error, not_registered};
190
:-(
{selected, [{Nick}]} -> {ok, Nick}
191 end.
192
193 %% Register nick
194 -spec set_nick(mongooseim:host_type(), muc_host(), client_jid(), mod_muc:nick()) -> ok | {error, term()}.
195 set_nick(HostType, MucHost, Jid, Nick) when is_binary(Nick), Nick =/= <<>> ->
196
:-(
CanUseNick = can_use_nick(HostType, MucHost, Jid, Nick),
197
:-(
store_nick_transaction(HostType, MucHost, Jid, Nick, CanUseNick).
198
199 %% Unregister nick
200 %% Unregistered nicks can be used by someone else
201 -spec unset_nick(mongooseim:host_type(), muc_host(), client_jid()) -> ok.
202 unset_nick(HostType, MucHost, Jid) ->
203
:-(
{UserU, UserS} = jid:to_lus(Jid),
204
:-(
execute_delete_nick(HostType, MucHost, UserU, UserS),
205
:-(
ok.
206
207 %% Transaction body functions
208
209 store_nick_transaction(_HostType, _MucHost, _Jid, _Nick, false) ->
210
:-(
{error, conflict};
211 store_nick_transaction(HostType, MucHost, Jid, Nick, true) ->
212
:-(
{LU, LS} = jid:to_lus(Jid),
213
:-(
InsertParams = [MucHost, LU, LS, Nick],
214
:-(
UpdateParams = [Nick],
215
:-(
UniqueKeyValues = [MucHost, LU, LS],
216
:-(
case rdbms_queries:execute_upsert(HostType, muc_nick_upsert,
217 InsertParams, UpdateParams, UniqueKeyValues) of
218
:-(
{updated, _} -> ok;
219
:-(
Error -> Error
220 end.
221
222 -spec store_room_transaction(mongooseim:host_type(), muc_host(), jid:luser(), binary(), term()) -> ok.
223 store_room_transaction(HostType, MucHost, RoomName, ExtOpts, Affs) ->
224
:-(
execute_insert_room(HostType, MucHost, RoomName, ExtOpts),
225
:-(
Result = execute_select_room_id(HostType, MucHost, RoomName),
226
:-(
RoomID = mongoose_rdbms:selected_to_integer(Result),
227
:-(
store_aff(HostType, RoomID, Affs),
228
:-(
ok.
229
230 store_aff(_HostType, _, undefined) ->
231
:-(
ok;
232 store_aff(HostType, RoomID, Affs) ->
233
:-(
F = fun({{UserU, UserS, Resource}, Aff}) ->
234
:-(
ExtAff = aff_atom2db(Aff),
235
:-(
execute_insert_aff(HostType, RoomID, UserU, UserS, Resource, ExtAff)
236 end,
237
:-(
lists:foreach(F, Affs).
238
239 forget_room_transaction(HostType, MucHost, RoomName) ->
240
:-(
case execute_select_room_id(HostType, MucHost, RoomName) of
241 {selected, [{ExtRoomID}]} ->
242
:-(
RoomID = mongoose_rdbms:result_to_integer(ExtRoomID),
243
:-(
execute_delete_affs(HostType, RoomID),
244
:-(
execute_delete_room(HostType, MucHost, RoomName),
245
:-(
ok;
246 {selected, []} ->
247
:-(
{error, not_exists}
248 end.
249
250 %% Execute call functions
251
252 -spec execute_insert_room(mongooseim:host_type(), muc_host(), jid:luser(), binary()) -> ok.
253 execute_insert_room(HostType, MucHost, RoomName, ExtOpts) ->
254
:-(
Args = [MucHost, RoomName, ExtOpts],
255
:-(
execute_successfully(HostType, muc_insert_room, Args),
256
:-(
ok.
257
258 -spec execute_insert_aff(mongooseim:host_type(), RoomID :: room_id(),
259 UserU :: jid:luser(), UserS :: jid:lserver(),
260 Res :: binary(), ExtAff :: pos_integer()) -> ok.
261 execute_insert_aff(HostType, RoomID, UserU, UserS, Res, ExtAff) ->
262
:-(
Args = [RoomID, UserU, UserS, Res, ExtAff],
263
:-(
execute_successfully(HostType, muc_insert_aff, Args),
264
:-(
ok.
265
266 -spec execute_select_aff(mongooseim:host_type(), room_id()) -> term().
267 execute_select_aff(HostType, RoomID) ->
268
:-(
execute_successfully(HostType, muc_select_aff, [RoomID]).
269
270 -spec execute_select_room_id(mongooseim:host_type(), muc_host(), jid:luser()) -> term().
271 execute_select_room_id(HostType, MucHost, RoomName) ->
272
:-(
execute_successfully(HostType, muc_select_room_id, [MucHost, RoomName]).
273
274 -spec execute_select_room(mongooseim:host_type(), muc_host(), jid:luser()) -> term().
275 execute_select_room(HostType, MucHost, RoomName) ->
276
:-(
execute_successfully(HostType, muc_select_room, [MucHost, RoomName]).
277
278 -spec execute_delete_affs(mongooseim:host_type(), room_id()) -> term().
279 execute_delete_affs(HostType, RoomID) ->
280
:-(
execute_successfully(HostType, muc_delete_aff, [RoomID]).
281
282 -spec execute_delete_room(mongooseim:host_type(), muc_host(), jid:luser()) -> term().
283 execute_delete_room(HostType, MucHost, RoomName) ->
284
:-(
execute_successfully(HostType, muc_delete_room, [MucHost, RoomName]).
285
286 -spec execute_select_rooms(mongooseim:host_type(), muc_host()) -> term().
287 execute_select_rooms(HostType, MucHost) ->
288
:-(
execute_successfully(HostType, muc_select_rooms, [MucHost]).
289
290 -spec execute_select_nick_user(mongooseim:host_type(), muc_host(), jid:luser(), mod_muc:nick()) -> term().
291 execute_select_nick_user(HostType, MucHost, UserS, Nick) ->
292
:-(
execute_successfully(HostType, muc_select_nick_user, [MucHost, UserS, Nick]).
293
294 -spec execute_select_nick(mongooseim:host_type(), muc_host(), jid:luser(), jid:lserver()) -> term().
295 execute_select_nick(HostType, MucHost, UserU, UserS) ->
296
:-(
execute_successfully(HostType, muc_select_nick, [MucHost, UserS, UserU]).
297
298 -spec execute_delete_nick(mongooseim:host_type(), muc_host(), jid:luser(), jid:lserver()) -> term().
299 execute_delete_nick(HostType, MucHost, UserU, UserS) ->
300
:-(
execute_successfully(HostType, muc_delete_nick, [MucHost, UserS, UserU]).
301
302 %% Conversion functions
303
304 -spec aff_atom2db(aff()) -> pos_integer().
305
:-(
aff_atom2db(owner) -> 1;
306
:-(
aff_atom2db({owner, _}) -> 1;
307
:-(
aff_atom2db(member) -> 2;
308
:-(
aff_atom2db({member, _}) -> 2;
309
:-(
aff_atom2db(admin) -> 3;
310
:-(
aff_atom2db({admin, _}) -> 3;
311
:-(
aff_atom2db(outcast) -> 4;
312
:-(
aff_atom2db({outcast, _}) -> 4;
313
:-(
aff_atom2db(_Other) -> 5.
314
315 -spec aff_db2atom(pos_integer()) -> aff().
316
:-(
aff_db2atom(1) -> owner;
317
:-(
aff_db2atom(2) -> member;
318
:-(
aff_db2atom(3) -> admin;
319
:-(
aff_db2atom(4) -> outcast;
320
:-(
aff_db2atom(5) -> none.
321
322 decode_opts(ExtOpts, Affs) ->
323
:-(
{Opts} = jiffy:decode(ExtOpts),
324
:-(
[{affiliations, decode_affs(Affs)} | keys_as_atoms(Opts)].
325
326 decode_affs(Affs) ->
327
:-(
[{{UserU, UserS, Res}, aff_db2atom(mongoose_rdbms:result_to_integer(Aff))}
328
:-(
|| {UserU, UserS, Res, Aff} <- Affs].
329
330 keys_as_atoms(KVs) ->
331
:-(
[{binary_to_existing_atom(Key, utf8), Value}
332
:-(
|| {Key, Value} <- KVs].
Line Hits Source