./ct_report/coverage/mod_privacy_rdbms.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% Copyright notice from original mod_privacy
3 %%%
4 %%% File : mod_privacy.erl
5 %%% Author : Alexey Shchepin <alexey@process-one.net>
6 %%% Purpose : jabber:iq:privacy support
7 %%% Created : 21 Jul 2003 by Alexey Shchepin <alexey@process-one.net>
8 %%%
9 %%%
10 %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
11 %%%
12 %%% This program is free software; you can redistribute it and/or
13 %%% modify it under the terms of the GNU General Public License as
14 %%% published by the Free Software Foundation; either version 2 of the
15 %%% License, or (at your option) any later version.
16 %%%
17 %%% This program is distributed in the hope that it will be useful,
18 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
19 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 %%% General Public License for more details.
21 %%%
22 %%% You should have received a copy of the GNU General Public License
23 %%% along with this program; if not, write to the Free Software
24 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 %%%
26 %%%----------------------------------------------------------------------
27
28 -module(mod_privacy_rdbms).
29 -author('alexey@process-one.net').
30 -author('arcusfelis@gmail.com').
31 -behaviour(mod_privacy_backend).
32
33 -export([init/2,
34 get_default_list/3,
35 get_list_names/3,
36 get_privacy_list/4,
37 set_default_list/4,
38 forget_default_list/3,
39 remove_privacy_list/4,
40 replace_privacy_list/5,
41 remove_user/3,
42 remove_domain/2]).
43
44 -include("mongoose.hrl").
45 -include("jlib.hrl").
46 -include("mod_privacy.hrl").
47
48 init(HostType, _Opts) ->
49 5 prepare_queries(HostType),
50 5 ok.
51
52 prepare_queries(HostType) ->
53 %% Queries to privacy_list table
54 5 mongoose_rdbms:prepare(privacy_list_get_id, privacy_list, [server, username, name],
55 <<"SELECT id FROM privacy_list WHERE server=? AND username=? AND name=?">>),
56 5 mongoose_rdbms:prepare(privacy_list_get_names, privacy_list, [server, username],
57 <<"SELECT name FROM privacy_list WHERE server=? AND username=?">>),
58 5 mongoose_rdbms:prepare(privacy_list_delete_by_name, privacy_list, [server, username, name],
59 <<"DELETE FROM privacy_list WHERE server=? AND username=? AND name=?">>),
60 5 mongoose_rdbms:prepare(privacy_list_delete_multiple, privacy_list, [server, username],
61 <<"DELETE FROM privacy_list WHERE server=? AND username=?">>),
62 5 mongoose_rdbms:prepare(privacy_list_insert, privacy_list, [server, username, name],
63 <<"INSERT INTO privacy_list(server, username, name) VALUES (?, ?, ?)">>),
64 %% Queries to privacy_default_list table
65 5 mongoose_rdbms:prepare(privacy_default_get_name, privacy_default_list, [server, username],
66 <<"SELECT name FROM privacy_default_list WHERE server=? AND username=?">>),
67 5 mongoose_rdbms:prepare(privacy_default_delete, privacy_default_list, [server, username],
68 <<"DELETE from privacy_default_list WHERE server=? AND username=?">>),
69 5 prepare_default_list_upsert(HostType),
70 %% Queries to privacy_list_data table
71 5 mongoose_rdbms:prepare(privacy_data_get_by_id, privacy_list_data, [id],
72 <<"SELECT ord, t, value, action, match_all, match_iq, "
73 "match_message, match_presence_in, match_presence_out "
74 "FROM privacy_list_data "
75 "WHERE id=? ORDER BY ord">>),
76 5 mongoose_rdbms:prepare(delete_data_by_id, privacy_list_data, [id],
77 <<"DELETE FROM privacy_list_data WHERE id=?">>),
78 5 mongoose_rdbms:prepare(privacy_data_delete, privacy_list_data, [id, ord],
79 <<"DELETE FROM privacy_list_data WHERE id=? AND ord=?">>),
80 5 mongoose_rdbms:prepare(privacy_data_update, privacy_list_data,
81 [t, value, action, match_all, match_iq,
82 match_message, match_presence_in, match_presence_out, id, ord],
83 <<"UPDATE privacy_list_data SET "
84 "t=?, value=?, action=?, match_all=?, match_iq=?, "
85 "match_message=?, match_presence_in=?, match_presence_out=? "
86 " WHERE id=? AND ord=?">>),
87 5 mongoose_rdbms:prepare(privacy_data_insert, privacy_list_data,
88 [id, ord, t, value, action, match_all, match_iq,
89 match_message, match_presence_in, match_presence_out],
90 <<"INSERT INTO privacy_list_data("
91 "id, ord, t, value, action, match_all, match_iq, "
92 "match_message, match_presence_in, match_presence_out) "
93 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)">>),
94 %% This query uses multiple tables
95 5 mongoose_rdbms:prepare(privacy_data_delete_user, privacy_list, [server, username],
96 <<"DELETE FROM privacy_list_data WHERE id IN "
97 "(SELECT id FROM privacy_list WHERE server=? AND username=?)">>),
98 %% delete domain queries
99 5 mongoose_rdbms:prepare(privacy_default_delete_domain, privacy_default_list, [server],
100 <<"DELETE from privacy_default_list WHERE server=?">>),
101 5 mongoose_rdbms:prepare(privacy_list_delete_domain, privacy_list, [server],
102 <<"DELETE FROM privacy_list WHERE server=?">>),
103 5 mongoose_rdbms:prepare(privacy_data_delete_domain, privacy_list, [server],
104 <<"DELETE FROM privacy_list_data WHERE id IN "
105 "(SELECT id FROM privacy_list WHERE server=?)">>),
106 5 ok.
107
108 prepare_default_list_upsert(HostType) ->
109 5 Fields = [<<"name">>],
110 5 Filter = [<<"server">>, <<"username">>],
111 5 rdbms_queries:prepare_upsert(HostType, privacy_default_upsert, privacy_default_list,
112 Filter ++ Fields, Fields, Filter).
113
114 default_list_upsert(HostType, LServer, LUser, Name) ->
115 41 InsertParams = [LServer, LUser, Name],
116 41 UpdateParams = [Name],
117 41 UniqueKeyValues = [LServer, LUser],
118 41 rdbms_queries:execute_upsert(HostType, privacy_default_upsert,
119 InsertParams, UpdateParams, UniqueKeyValues).
120
121 get_default_list(HostType, LUser, LServer) ->
122 2524 case get_default_list_name(HostType, LUser, LServer) of
123 none ->
124 2454 {error, not_found};
125 Default ->
126 70 case get_privacy_list(HostType, LUser, LServer, Default) of
127 {ok, List} ->
128 70 {ok, {Default, List}};
129 {error, Reason} ->
130
:-(
{error, Reason}
131 end
132 end.
133
134 get_list_names(HostType, LUser, LServer) ->
135 3 Default = get_default_list_name(HostType, LUser, LServer),
136 3 Names = get_list_names_only(HostType, LUser, LServer),
137 3 {ok, {Default, Names}}.
138
139 get_default_list_name(HostType, LUser, LServer) ->
140 2527 try execute_privacy_default_get_name(HostType, LServer, LUser) of
141 {selected, []} ->
142 2456 none;
143 {selected, [{DefName}]} ->
144 71 DefName;
145 Other ->
146
:-(
?LOG_ERROR(#{what => privacy_get_default_list_name_failed,
147
:-(
user => LUser, server => LServer, reason => Other}),
148
:-(
none
149 catch
150 Class:Reason:StackTrace ->
151
:-(
?LOG_ERROR(#{what => privacy_get_default_list_name_failed,
152 user => LUser, server => LServer,
153
:-(
class => Class, reason => Reason, stacktrace => StackTrace}),
154
:-(
none
155 end.
156
157 get_list_names_only(HostType, LUser, LServer) ->
158 3 try execute_privacy_list_get_names(HostType, LServer, LUser) of
159 {selected, Names} ->
160 3 [Name || {Name} <- Names];
161 Other ->
162
:-(
?LOG_ERROR(#{what => privacy_get_list_names_only_failed,
163
:-(
user => LUser, server => LServer, reason => Other}),
164
:-(
[]
165 catch
166 Class:Reason:StackTrace ->
167
:-(
?LOG_ERROR(#{what => privacy_get_list_names_only_failed,
168 user => LUser, server => LServer,
169
:-(
class => Class, reason => Reason, stacktrace => StackTrace}),
170
:-(
[]
171 end.
172
173 get_privacy_list(HostType, LUser, LServer, Name) ->
174 199 try execute_privacy_list_get_id(HostType, LServer, LUser, Name) of
175 {selected, []} ->
176 20 {error, not_found};
177 {selected, [{ID}]} ->
178 179 IntID = mongoose_rdbms:result_to_integer(ID),
179 179 get_privacy_list_by_id(HostType, LUser, LServer, Name, IntID, LServer);
180 Other ->
181
:-(
?LOG_ERROR(#{what => privacy_get_privacy_list_failed,
182 user => LUser, server => LServer, list_name => Name,
183
:-(
reason => Other}),
184
:-(
{error, Other}
185 catch
186 Class:Reason:StackTrace ->
187
:-(
?LOG_ERROR(#{what => privacy_get_privacy_list_failed,
188 user => LUser, server => LServer, list_name => Name,
189
:-(
class => Class, reason => Reason, stacktrace => StackTrace}),
190
:-(
{error, Reason}
191 end.
192
193 get_privacy_list_by_id(HostType, LUser, LServer, Name, ID, LServer) when is_integer(ID) ->
194 179 try execute_privacy_data_get_by_id(HostType, ID) of
195 {selected, Rows} ->
196 179 {ok, raw_to_items(Rows)};
197 Other ->
198
:-(
?LOG_ERROR(#{what => privacy_get_privacy_list_by_id_failed,
199 user => LUser, server => LServer, list_name => Name, list_id => ID,
200
:-(
reason => Other}),
201
:-(
{error, Other}
202 catch
203 Class:Reason:StackTrace ->
204
:-(
?LOG_ERROR(#{what => privacy_get_privacy_list_by_id_failed,
205 user => LUser, server => LServer, list_name => Name, list_id => ID,
206
:-(
class => Class, reason => Reason, stacktrace => StackTrace}),
207
:-(
{error, Reason}
208 end.
209
210 %% @doc Set no default list for user.
211 forget_default_list(HostType, LUser, LServer) ->
212 1 try execute_privacy_default_delete(HostType, LServer, LUser) of
213 {updated, _} ->
214 1 ok;
215 Other ->
216
:-(
?LOG_ERROR(#{what => privacy_forget_default_list_failed,
217
:-(
user => LUser, server => LServer, reason => Other}),
218
:-(
{error, Other}
219 catch
220 Class:Reason:StackTrace ->
221
:-(
?LOG_ERROR(#{what => privacy_forget_default_list_failed,
222 user => LUser, server => LServer,
223
:-(
class => Class, reason => Reason, stacktrace => StackTrace}),
224
:-(
{error, Reason}
225 end.
226
227 set_default_list(HostType, LUser, LServer, Name) ->
228 42 F = fun() -> set_default_list_t(HostType, LServer, LUser, Name) end,
229 42 case rdbms_queries:sql_transaction(HostType, F) of
230 {atomic, ok} ->
231 41 ok;
232 {atomic, {error, Reason}} ->
233 1 {error, Reason};
234 {aborted, Reason} ->
235
:-(
{error, {aborted, Reason}};
236 {error, Reason} ->
237
:-(
{error, Reason}
238 end.
239
240 set_default_list_t(HostType, LServer, LUser, Name) ->
241 42 case execute_privacy_list_get_names(HostType, LServer, LUser) of
242 {selected, []} ->
243 1 {error, not_found};
244 {selected, Names} ->
245 41 case lists:member({Name}, Names) of
246 true ->
247 41 default_list_upsert(HostType, LServer, LUser, Name),
248 41 ok;
249 false ->
250
:-(
{error, not_found}
251 end
252 end.
253
254 remove_privacy_list(HostType, LUser, LServer, Name) ->
255 1 F = fun() ->
256 1 case execute_privacy_default_get_name(HostType, LServer, LUser) of
257 {selected, [{Name}]} -> %% Matches Name variable
258
:-(
{error, conflict};
259 {selected, _} ->
260 1 execute_privacy_list_delete_by_name(HostType, LServer, LUser, Name),
261 1 ok
262 end
263 end,
264 1 case rdbms_queries:sql_transaction(HostType, F) of
265 {atomic, {error, _} = Error} ->
266
:-(
Error;
267 {atomic, ok} ->
268 1 ok;
269 {aborted, Reason} ->
270
:-(
{error, {aborted, Reason}};
271 {error, Reason} ->
272
:-(
{error, Reason}
273 end.
274
275 replace_privacy_list(HostType, LUser, LServer, Name, List) ->
276 77 Rows = lists:map(fun item_to_raw/1, List),
277 77 F = fun() ->
278 77 ResultID = case execute_privacy_list_get_id(HostType, LServer, LUser, Name) of
279 {selected, []} ->
280 64 execute_privacy_list_insert(HostType, LServer, LUser, Name),
281 64 {selected, [{I}]} = execute_privacy_list_get_id(HostType, LServer, LUser, Name),
282 64 I;
283 {selected, [{I}]} ->
284 13 I
285 end,
286 77 ID = mongoose_rdbms:result_to_integer(ResultID),
287 77 replace_data_rows(HostType, ID, Rows),
288 77 ok
289 end,
290 77 {atomic, ok} = mongoose_rdbms:transaction_with_delayed_retry(HostType, F, #{retries => 5, delay => 100}),
291 77 ok.
292
293 remove_domain(HostType, LServer) ->
294
:-(
F = fun() -> remove_domain_t(HostType, LServer) end,
295
:-(
rdbms_queries:sql_transaction(HostType, F).
296
297 remove_domain_t(HostType, LServer) ->
298
:-(
mongoose_rdbms:execute_successfully(HostType, privacy_data_delete_domain, [LServer]),
299
:-(
mongoose_rdbms:execute_successfully(HostType, privacy_list_delete_domain, [LServer]),
300
:-(
mongoose_rdbms:execute_successfully(HostType, privacy_default_delete_domain, [LServer]).
301
302 remove_user(HostType, LUser, LServer) ->
303 114 F = fun() -> remove_user_t(HostType, LUser, LServer) end,
304 114 rdbms_queries:sql_transaction(HostType, F).
305
306 remove_user_t(HostType, LUser, LServer) ->
307 114 mongoose_rdbms:execute_successfully(HostType, privacy_data_delete_user, [LServer, LUser]),
308 114 mongoose_rdbms:execute_successfully(HostType, privacy_list_delete_multiple, [LServer, LUser]),
309 114 execute_privacy_default_delete(HostType, LServer, LUser).
310
311 execute_privacy_list_get_id(HostType, LServer, LUser, Name) ->
312 340 mongoose_rdbms:execute_successfully(HostType, privacy_list_get_id, [LServer, LUser, Name]).
313
314 execute_privacy_default_get_name(HostType, LServer, LUser) ->
315 2528 mongoose_rdbms:execute_successfully(HostType, privacy_default_get_name, [LServer, LUser]).
316
317 execute_privacy_list_get_names(HostType, LServer, LUser) ->
318 45 mongoose_rdbms:execute_successfully(HostType, privacy_list_get_names, [LServer, LUser]).
319
320 execute_privacy_data_get_by_id(HostType, ID) ->
321 250 mongoose_rdbms:execute_successfully(HostType, privacy_data_get_by_id, [ID]).
322
323 execute_privacy_default_delete(HostType, LServer, LUser) ->
324 115 mongoose_rdbms:execute_successfully(HostType, privacy_default_delete, [LServer, LUser]).
325
326 execute_privacy_list_delete_by_name(HostType, LServer, LUser, Name) ->
327 1 mongoose_rdbms:execute_successfully(HostType, privacy_list_delete_by_name, [LServer, LUser, Name]).
328
329 execute_privacy_list_insert(HostType, LServer, LUser, Name) ->
330 64 mongoose_rdbms:execute_successfully(HostType, privacy_list_insert, [LServer, LUser, Name]).
331
332 execute_delete_data_by_id(HostType, ID) ->
333 6 mongoose_rdbms:execute_successfully(HostType, delete_data_by_id, [ID]).
334
335 replace_data_rows(HostType, ID, []) when is_integer(ID) ->
336 %% Just remove the data, nothing should be inserted
337 6 execute_delete_data_by_id(HostType, ID);
338 replace_data_rows(HostType, ID, Rows) when is_integer(ID) ->
339 71 {selected, OldRows} = execute_privacy_data_get_by_id(HostType, ID),
340 71 New = lists:sort(Rows),
341 71 Old = lists:sort([tuple_to_list(Row) || Row <- OldRows]),
342 71 Diff = diff_rows(ID, New, Old, []),
343 71 F = fun({Q, Args}) -> mongoose_rdbms:execute_successfully(HostType, Q, Args) end,
344 71 lists:foreach(F, Diff),
345 71 ok.
346
347 %% We assume that there are no record duplicates with the same Order.
348 %% It's checked in the main module for the New argument.
349 %% It's checked by the database for the Old argument.
350 diff_rows(ID, [H|New], [H|Old], Ops) ->
351
:-(
diff_rows(ID, New, Old, Ops); %% Not modified
352 diff_rows(ID, [NewH|NewT] = New, [OldH|OldT] = Old, Ops) ->
353 11 NewOrder = hd(NewH),
354 11 OldOrder = hd(OldH),
355 11 if NewOrder =:= OldOrder ->
356 9 Op = {privacy_data_update, tl(NewH) ++ [ID, OldOrder]},
357 9 diff_rows(ID, NewT, OldT, [Op|Ops]);
358 NewOrder > OldOrder ->
359 1 Op = {privacy_data_delete, [ID, OldOrder]},
360 1 diff_rows(ID, New, OldT, [Op|Ops]);
361 true ->
362 1 Op = {privacy_data_insert, [ID|NewH]},
363 1 diff_rows(ID, NewT, Old, [Op|Ops])
364 end;
365 diff_rows(ID, [], [OldH|OldT], Ops) ->
366 1 OldOrder = hd(OldH),
367 1 Op = {privacy_data_delete, [ID, OldOrder]},
368 1 diff_rows(ID, [], OldT, [Op|Ops]);
369 diff_rows(ID, [NewH|NewT], [], Ops) ->
370 78 Op = {privacy_data_insert, [ID|NewH]},
371 78 diff_rows(ID, NewT, [], [Op|Ops]);
372 diff_rows(_ID, [], [], Ops) ->
373 71 Ops.
374
375 %% Encoding/decoding pure functions
376
377 raw_to_items(Rows) ->
378 179 [raw_to_item(Row) || Row <- Rows].
379
380 raw_to_item({ExtOrder, ExtType, ExtValue, ExtAction,
381 ExtMatchAll, ExtMatchIQ, ExtMatchMessage,
382 ExtMatchPresenceIn, ExtMatchPresenceOut}) ->
383 201 Type = decode_type(mongoose_rdbms:character_to_integer(ExtType)),
384 201 #listitem{type = Type,
385 value = decode_value(Type, ExtValue),
386 action = decode_action(mongoose_rdbms:character_to_integer(ExtAction)),
387 order = mongoose_rdbms:result_to_integer(ExtOrder),
388 match_all = mongoose_rdbms:to_bool(ExtMatchAll),
389 match_iq = mongoose_rdbms:to_bool(ExtMatchIQ),
390 match_message = mongoose_rdbms:to_bool(ExtMatchMessage),
391 match_presence_in = mongoose_rdbms:to_bool(ExtMatchPresenceIn),
392 match_presence_out = mongoose_rdbms:to_bool(ExtMatchPresenceOut)}.
393
394 %% Encodes for privacy_data_insert query (but without ID)
395 -spec item_to_raw(mod_privacy:list_item()) -> list(term()).
396 item_to_raw(#listitem{type = Type,
397 value = Value,
398 action = Action,
399 order = Order,
400 match_all = MatchAll,
401 match_iq = MatchIQ,
402 match_message = MatchMessage,
403 match_presence_in = MatchPresenceIn,
404 match_presence_out = MatchPresenceOut}) ->
405 88 ExtType = encode_type(Type),
406 88 ExtValue = encode_value(Type, Value),
407 88 ExtAction = encode_action(Action),
408 88 Bools = [MatchAll, MatchIQ, MatchMessage, MatchPresenceIn, MatchPresenceOut],
409 88 ExtBools = [encode_boolean(X) || X <- Bools],
410 88 [Order, ExtType, ExtValue, ExtAction | ExtBools].
411
412 88 encode_boolean(true) -> 1;
413 352 encode_boolean(false) -> 0.
414
415 5 encode_action(allow) -> <<"a">>;
416 52 encode_action(deny) -> <<"d">>;
417 31 encode_action(block) -> <<"b">>.
418
419 9 decode_action($a) -> allow;
420 107 decode_action($d) -> deny;
421 85 decode_action($b) -> block.
422
423 2 encode_subscription(none) -> <<"none">>;
424 2 encode_subscription(both) -> <<"both">>;
425 2 encode_subscription(from) -> <<"from">>;
426 2 encode_subscription(to) -> <<"to">>.
427
428 4 decode_subscription(<<"none">>) -> none;
429 4 decode_subscription(<<"both">>) -> both;
430 4 decode_subscription(<<"from">>) -> from;
431 4 decode_subscription(<<"to">>) -> to.
432
433 29 encode_type(none) -> <<"n">>;
434 50 encode_type(jid) -> <<"j">>;
435 1 encode_type(group) -> <<"g">>;
436 8 encode_type(subscription) -> <<"s">>.
437
438 70 decode_type($n) -> none;
439 113 decode_type($j) -> jid;
440 2 decode_type($g) -> group;
441 16 decode_type($s) -> subscription.
442
443 29 encode_value(none, _Value) -> <<>>;
444 50 encode_value(jid, Value) -> jid:to_binary(Value);
445 1 encode_value(group, Value) -> Value;
446 8 encode_value(subscription, Value) -> encode_subscription(Value).
447
448 70 decode_value(none, _) -> none;
449 113 decode_value(jid, BinJid) -> jid:to_lower(jid:from_binary(BinJid));
450 2 decode_value(group, Group) -> Group;
451 16 decode_value(subscription, ExtSub) -> decode_subscription(ExtSub).
Line Hits Source