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