./ct_report/coverage/mod_privacy.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : mod_privacy.erl
3 %%% Author : Alexey Shchepin <alexey@process-one.net>
4 %%% Purpose : jabber:iq:privacy support
5 %%% Created : 21 Jul 2003 by Alexey Shchepin <alexey@process-one.net>
6 %%%
7 %%%
8 %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
9 %%%
10 %%% This program is free software; you can redistribute it and/or
11 %%% modify it under the terms of the GNU General Public License as
12 %%% published by the Free Software Foundation; either version 2 of the
13 %%% License, or (at your option) any later version.
14 %%%
15 %%% This program is distributed in the hope that it will be useful,
16 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 %%% General Public License for more details.
19 %%%
20 %%% You should have received a copy of the GNU General Public License
21 %%% along with this program; if not, write to the Free Software
22 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 %%%
24 %%%----------------------------------------------------------------------
25
26 -module(mod_privacy).
27 -author('alexey@process-one.net').
28 -xep([{xep, 16}, {version, "1.7"}]).
29 -xep([{xep, 126}, {version, "1.1"}]).
30 -behaviour(gen_mod).
31 -behaviour(mongoose_module_metrics).
32
33 %% gen_mod
34 -export([start/2, stop/1, hooks/1, deps/2, config_spec/0, supported_features/0]).
35
36 %% Hook handlers
37 -export([process_iq_set/3,
38 process_iq_get/3,
39 get_user_list/3,
40 check_packet/3,
41 remove_user/3,
42 remove_domain/3,
43 updated_list/3,
44 disco_local_features/3]).
45
46 -export([user_send_message_or_presence/3,
47 user_send_iq/3,
48 user_receive_message/3,
49 user_receive_presence/3,
50 user_receive_iq/3,
51 foreign_event/3]).
52
53 %% to be used by mod_blocking only
54 -export([do_user_send_iq/4]).
55
56 -export([config_metrics/1]).
57
58 -include("jlib.hrl").
59 -include("mod_privacy.hrl").
60 -include("mongoose_config_spec.hrl").
61 -include("mongoose_logger.hrl").
62
63 -export_type([list_name/0]).
64 -export_type([list_item/0]).
65 -export_type([privacy_item_type/0]).
66
67 -type maybe_send() :: send | ignore.
68 -type list_name() :: binary() | none.
69 -type list_item() :: #listitem{}.
70 -type privacy_item_type() :: none | jid | group | subscription.
71
72 %% ------------------------------------------------------------------
73 %% gen_mod callbacks
74 %% ------------------------------------------------------------------
75
76 -spec start(mongooseim:host_type(), gen_mod:module_opts()) -> ok.
77 start(HostType, Opts) ->
78 5 mod_privacy_backend:init(HostType, Opts).
79
80 -spec stop(mongooseim:host_type()) -> ok.
81 stop(_HostType) ->
82 5 ok.
83
84 deps(_HostType, _Opts) ->
85 27 [{mod_presence, #{}, hard}].
86
87 config_spec() ->
88 372 #section{
89 items = #{<<"backend">> => #option{type = atom,
90 validate = {module, mod_privacy}}},
91 defaults = #{<<"backend">> => mnesia}
92 }.
93 -spec supported_features() -> [atom()].
94 supported_features() ->
95 7 [dynamic_domains].
96
97 -spec hooks(mongooseim:host_type()) -> gen_hook:hook_list().
98 hooks(HostType) ->
99 10 [{disco_local_features, HostType, fun ?MODULE:disco_local_features/3, #{}, 98},
100 {privacy_iq_get, HostType, fun ?MODULE:process_iq_get/3, #{}, 50},
101 {privacy_iq_set, HostType, fun ?MODULE:process_iq_set/3, #{}, 50},
102 {privacy_get_user_list, HostType, fun ?MODULE:get_user_list/3, #{}, 50},
103 {privacy_check_packet, HostType, fun ?MODULE:check_packet/3, #{}, 50},
104 {privacy_updated_list, HostType, fun ?MODULE:updated_list/3, #{}, 50},
105 {remove_user, HostType, fun ?MODULE:remove_user/3, #{}, 50},
106 {remove_domain, HostType, fun ?MODULE:remove_domain/3, #{}, 50},
107 {anonymous_purge_hook, HostType, fun ?MODULE:remove_user/3, #{}, 50}
108 | c2s_hooks(HostType)].
109
110 -spec c2s_hooks(mongooseim:host_type()) -> gen_hook:hook_list(mongoose_c2s_hooks:fn()).
111 c2s_hooks(HostType) ->
112 10 [
113 {user_send_message, HostType, fun ?MODULE:user_send_message_or_presence/3, #{}, 10},
114 {user_send_presence, HostType, fun ?MODULE:user_send_message_or_presence/3, #{}, 10},
115 {user_send_iq, HostType, fun ?MODULE:user_send_iq/3, #{}, 50},
116 {user_receive_message, HostType, fun ?MODULE:user_receive_message/3, #{}, 10},
117 {user_receive_presence, HostType, fun ?MODULE:user_receive_presence/3, #{}, 10},
118 {user_receive_iq, HostType, fun ?MODULE:user_receive_iq/3, #{}, 10},
119 {foreign_event, HostType, fun ?MODULE:foreign_event/3, #{}, 50}
120 ].
121
122 %% ------------------------------------------------------------------
123 %% Handlers
124 %% ------------------------------------------------------------------
125
126 -spec user_send_message_or_presence(mongoose_acc:t(), mongoose_c2s_hooks:params(), gen_hook:extra()) ->
127 mongoose_c2s_hooks:result().
128 user_send_message_or_presence(Acc, #{c2s_data := StateData}, _Extra) ->
129 648 do_privacy_check_send(Acc, StateData).
130
131 -spec user_send_iq(mongoose_acc:t(), mongoose_c2s_hooks:params(), map()) ->
132 mongoose_c2s_hooks:result().
133 user_send_iq(Acc, #{c2s_data := StateData}, #{host_type := HostType}) ->
134 672 case mongoose_iq:info(Acc) of
135 {#iq{xmlns = ?NS_PRIVACY, type = Type} = IQ, Acc1} when Type == get; Type == set ->
136 165 do_user_send_iq(Acc1, StateData, HostType, IQ);
137 _ ->
138 507 do_privacy_check_send(Acc, StateData)
139 end.
140
141 -spec user_receive_message(mongoose_acc:t(), mongoose_c2s_hooks:params(), gen_hook:extra()) ->
142 mongoose_c2s_hooks:result().
143 user_receive_message(Acc, #{c2s_data := StateData}, _Extra) ->
144 267 do_privacy_check_receive(Acc, StateData, send).
145
146 -spec user_receive_presence(mongoose_acc:t(), mongoose_c2s_hooks:params(), map()) ->
147 mongoose_c2s_hooks:result().
148 user_receive_presence(Acc, #{c2s_data := StateData}, _Extra) ->
149 1011 case mongoose_acc:stanza_type(Acc) of
150 2 <<"error">> -> {ok, Acc};
151 469 <<"probe">> -> {ok, Acc};
152 540 _ -> do_privacy_check_receive(Acc, StateData, ignore)
153 end.
154
155 -spec user_receive_iq(mongoose_acc:t(), mongoose_c2s_hooks:params(), gen_hook:extra()) ->
156 mongoose_c2s_hooks:result().
157 user_receive_iq(Acc, #{c2s_data := StateData}, _Extra) ->
158 766 From = mongoose_acc:from_jid(Acc),
159 766 Me = mongoose_c2s:get_jid(StateData),
160 766 case {mongoose_iq:info(Acc), jid:are_bare_equal(From, Me)} of
161 {{#iq{xmlns = ?NS_PRIVACY}, Acc1}, true} ->
162 109 {ok, Acc1};
163 {{#iq{type = Type}, Acc1}, _} when Type =:= get; Type =:= set ->
164 52 do_privacy_check_receive(Acc1, StateData, send);
165 {{_, Acc1}, _} ->
166 605 do_privacy_check_receive(Acc1, StateData, ignore)
167 end.
168
169 -spec foreign_event(mongoose_acc:t(), mongoose_c2s_hooks:params(), gen_hook:extra()) ->
170 mongoose_c2s_hooks:result().
171 foreign_event(Acc, #{c2s_data := StateData,
172 event_type := cast,
173 event_tag := ?MODULE,
174 event_content := {privacy_list, PrivList, PrivListName}},
175 #{host_type := HostType}) ->
176 53 {stop, handle_new_privacy_list(Acc, StateData, HostType, PrivList, PrivListName)};
177 foreign_event(Acc, _Params, _Extra) ->
178 29 {ok, Acc}.
179
180 -spec do_privacy_check_send(mongoose_acc:t(), mongoose_c2s:data()) ->
181 mongoose_c2s_hooks:result().
182 do_privacy_check_send(Acc, StateData) ->
183 1155 case s_privacy_check_packet(Acc, StateData, out) of
184 {allow, Acc1} ->
185 1149 {ok, Acc1};
186 {block, Acc1} ->
187 4 {stop, send_back_error(Acc1, not_acceptable_blocked, send)};
188 {deny, Acc1} ->
189 2 {stop, send_back_error(Acc1, not_acceptable_cancel, send)}
190 end.
191
192 -spec do_privacy_check_receive(mongoose_acc:t(), mongoose_c2s:data(), maybe_send()) ->
193 mongoose_c2s_hooks:result().
194 do_privacy_check_receive(Acc, StateData, MaybeSendError) ->
195 1464 case s_privacy_check_packet(Acc, StateData, in) of
196 {allow, Acc1} ->
197 1422 {ok, Acc1};
198 {_, Acc1} ->
199 42 {stop, send_back_error(Acc1, service_unavailable, MaybeSendError)}
200 end.
201
202 -spec do_user_send_iq(mongoose_acc:t(), mongoose_c2s:data(), mongooseim:host_type(), jlib:iq()) ->
203 mongoose_c2s_hooks:result().
204 do_user_send_iq(Acc1, StateData, HostType, #iq{type = Type, sub_el = SubEl} = IQ) ->
205 201 FromJid = mongoose_acc:from_jid(Acc1),
206 201 ToJid = mongoose_acc:to_jid(Acc1),
207 201 Acc2 = process_privacy_iq(Acc1, HostType, Type, ToJid, StateData),
208 201 Res = mongoose_acc:get(hook, result,
209 {error, mongoose_xmpp_errors:feature_not_implemented(
210 <<"en">>, <<"Failed to handle the privacy IQ request in
211 c2s">>)}, Acc2),
212 201 IQRes = case Res of
213 {result, Result} ->
214 125 IQ#iq{type = result, sub_el = Result};
215 {result, Result, _} ->
216 70 IQ#iq{type = result, sub_el = Result};
217 {error, Error} ->
218 6 IQ#iq{type = error, sub_el = [SubEl, Error]}
219 end,
220 201 ejabberd_router:route(ToJid, FromJid, Acc2, jlib:iq_to_xml(IQRes)),
221 201 {stop, Acc2}.
222
223 -spec handle_new_privacy_list(
224 mongoose_acc:t(), mongoose_c2s:data(), mongooseim:host_type(), term(), term()) ->
225 mongoose_acc:t().
226 handle_new_privacy_list(Acc, StateData, HostType, PrivList, PrivListName) ->
227 53 OldPrivList = get_handler(StateData),
228 53 case mongoose_hooks:privacy_updated_list(HostType, OldPrivList, PrivList) of
229 false ->
230
:-(
Acc;
231 NewPrivList ->
232 53 PrivPushIQ = privacy_list_push_iq(PrivListName),
233 53 Jid = mongoose_c2s:get_jid(StateData),
234 53 BareJid = jid:to_bare(Jid),
235 53 PrivPushEl = jlib:replace_from_to(BareJid, Jid, jlib:iq_to_xml(PrivPushIQ)),
236 53 maybe_update_presence(Acc, StateData, OldPrivList, NewPrivList),
237 53 AccParams = #{from_jid => BareJid, to_jid => Jid, element => PrivPushEl},
238 53 PrivPushAcc = mongoose_acc:update_stanza(AccParams, Acc),
239 53 ToAcc = [{route, PrivPushAcc}, {state_mod, {?MODULE, NewPrivList}}],
240 53 mongoose_c2s_acc:to_acc_many(Acc, ToAcc)
241 end.
242
243 -spec process_privacy_iq(Acc :: mongoose_acc:t(),
244 mongooseim:host_type(),
245 Type :: get | set,
246 ToJid :: jid:jid(),
247 StateData :: mongoose_c2s:data()) -> mongoose_acc:t().
248 process_privacy_iq(Acc, HostType, get, ToJid, StateData) ->
249 62 PrivacyList = get_handler(StateData),
250 62 From = mongoose_acc:from_jid(Acc),
251 62 {IQ, Acc1} = mongoose_iq:info(Acc),
252 62 mongoose_hooks:privacy_iq_get(HostType, Acc1, From, ToJid, IQ, PrivacyList);
253 process_privacy_iq(Acc, HostType, set, ToJid, StateData) ->
254 139 OldPrivList = get_handler(StateData),
255 139 From = mongoose_acc:from_jid(Acc),
256 139 {IQ, Acc1} = mongoose_iq:info(Acc),
257 139 Acc2 = mongoose_hooks:privacy_iq_set(HostType, Acc1, From, ToJid, IQ),
258 139 case mongoose_acc:get(hook, result, undefined, Acc2) of
259 {result, _, NewPrivList} ->
260 70 maybe_update_presence(Acc2, StateData, OldPrivList, NewPrivList),
261 70 mongoose_c2s_acc:to_acc(Acc2, state_mod, {?MODULE, NewPrivList});
262 _ ->
263 69 Acc2
264 end.
265
266 maybe_update_presence(Acc, StateData, OldList, NewList) ->
267 123 Jid = mongoose_c2s:get_jid(StateData),
268 123 Presences = mod_presence:maybe_get_handler(StateData),
269 123 FromS = mod_presence:get(Presences, s_from),
270 % Our own jid is added to pres_f, even though we're not a "contact", so for
271 % the purposes of this check we don't want it:
272 123 SelfJID = jid:to_bare(Jid),
273 123 FromsExceptSelf = maps:remove(SelfJID, FromS),
274 123 maps:fold(
275 fun(T, _, Ac) ->
276 7 send_unavail_if_newly_blocked(Ac, Jid, T, OldList, NewList, StateData)
277 end, Acc, FromsExceptSelf).
278
279 send_unavail_if_newly_blocked(Acc, Jid, ContactJID, OldList, NewList, StateData) ->
280 7 Packet = #xmlel{name = <<"presence">>,
281 attrs = [{<<"type">>, <<"unavailable">>}]},
282 %% WARNING: we can not use accumulator to cache privacy check result - this is
283 %% the only place where the list to check against changes
284 7 {OldResult, _} = p_privacy_check_packet(Packet, Jid, ContactJID, out, OldList, StateData),
285 7 {NewResult, _} = p_privacy_check_packet(Packet, Jid, ContactJID, out, NewList, StateData),
286 7 do_send_unavail_if_newly_blocked(Acc, OldResult, NewResult, Jid, ContactJID, Packet).
287
288 do_send_unavail_if_newly_blocked(Acc, allow, deny, From, To, Packet) ->
289 2 ejabberd_router:route(From, To, Acc, Packet);
290 do_send_unavail_if_newly_blocked(Acc, _, _, _, _, _) ->
291 5 Acc.
292
293 -spec p_privacy_check_packet(Packet :: exml:element() | mongoose_acc:t(),
294 From :: jid:jid(),
295 To :: jid:jid(),
296 Dir :: mongoose_privacy:direction(),
297 PrivList :: mongoose_privacy:userlist(),
298 StateData :: mongoose_c2s:data()) ->
299 {mongoose_privacy:decision(), mongoose_acc:t()}.
300 p_privacy_check_packet(#xmlel{} = Packet, From, To, Dir, PrivList, StateData) ->
301 14 LServer = mongoose_c2s:get_lserver(StateData),
302 14 HostType = mongoose_c2s:get_host_type(StateData),
303 14 Acc = mongoose_acc:new(#{host_type => HostType, lserver => LServer, location => ?LOCATION,
304 from_jid => From, to_jid => To, element => Packet}),
305 14 mongoose_privacy:privacy_check_packet(Acc, From, PrivList, To, Dir).
306
307 -spec s_privacy_check_packet(mongoose_acc:t(), mongoose_c2s:data(), mongoose_privacy:direction()) ->
308 {mongoose_privacy:decision(), mongoose_acc:t()}.
309 s_privacy_check_packet(Acc, StateData, Dir) ->
310 2619 To = mongoose_acc:to_jid(Acc),
311 2619 Jid = mongoose_c2s:get_jid(StateData),
312 2619 PrivList = get_handler(StateData),
313 2619 mongoose_privacy:privacy_check_packet(Acc, Jid, PrivList, To, Dir).
314
315 -spec send_back_error(mongoose_acc:t(), mongoose_xmpp_errors:stanza_error(), maybe_send()) ->
316 mongoose_acc:t().
317 send_back_error(Acc1, ErrorType, send) ->
318 40 Acc2 = mod_amp:check_packet(Acc1, delivery_failed),
319 40 {From, To, _El} = mongoose_acc:packet(Acc2),
320 40 Error = mongoose_xmpp_errors:ErrorType(),
321 40 {Acc3, Err} = jlib:make_error_reply(Acc2, Error),
322 40 ejabberd_router:route(To, From, Acc3, Err),
323 40 Acc3;
324 send_back_error(Acc, _, _) ->
325 8 Acc.
326
327 -spec get_handler(mongoose_c2s:data()) -> mongoose_privacy:userlist() | {error, not_found}.
328 get_handler(StateData) ->
329 2873 case mongoose_c2s:get_mod_state(StateData, ?MODULE) of
330 2470 {error, not_found} -> get_privacy_list(StateData);
331 403 {ok, Handler} -> Handler
332 end.
333
334 -spec get_privacy_list(mongoose_c2s:data()) -> mongoose_privacy:userlist().
335 get_privacy_list(StateData) ->
336 2470 Jid = mongoose_c2s:get_jid(StateData),
337 2470 HostType = mongoose_c2s:get_host_type(StateData),
338 2470 mongoose_hooks:privacy_get_user_list(HostType, Jid).
339
340 -spec privacy_list_push_iq(binary()) -> jlib:iq().
341 privacy_list_push_iq(PrivListName) ->
342 53 #iq{type = set, xmlns = ?NS_PRIVACY,
343 id = <<"push", (mongoose_bin:gen_from_crypto())/binary>>,
344 sub_el = [#xmlel{name = <<"query">>,
345 attrs = [{<<"xmlns">>, ?NS_PRIVACY}],
346 children = [#xmlel{name = <<"list">>,
347 attrs = [{<<"name">>, PrivListName}]}]}]}.
348
349 -spec disco_local_features(mongoose_disco:feature_acc(), map(), map()) ->
350 {ok, mongoose_disco:feature_acc()}.
351 disco_local_features(Acc = #{node := <<>>}, _, _) ->
352 3 {ok, mongoose_disco:add_features([?NS_PRIVACY], Acc)};
353 disco_local_features(Acc, _, _) ->
354 1 {ok, Acc}.
355
356 -spec process_iq_get(Acc, Params, Extra) -> {ok, Acc} when
357 Acc :: mongoose_acc:t(),
358 Params :: #{from := jid:jid(),
359 iq := jlib:iq(),
360 priv_list := mongoose_privacy:userlist()},
361 Extra :: gen_hook:extra().
362 process_iq_get(Acc,
363 #{from := #jid{luser = LUser, lserver = LServer},
364 iq := #iq{xmlns = ?NS_PRIVACY, sub_el = #xmlel{children = Els}},
365 priv_list := #userlist{name = Active}},
366 #{host_type := HostType}) ->
367 54 Res = case xml:remove_cdata(Els) of
368 [] ->
369 3 process_lists_get(Acc, HostType, LUser, LServer, Active);
370 [#xmlel{name = Name, attrs = Attrs}] ->
371 50 case Name of
372 <<"list">> ->
373 50 ListName = xml:get_attr(<<"name">>, Attrs),
374 50 process_list_get(Acc, HostType, LUser, LServer, ListName);
375 _ ->
376
:-(
{error, mongoose_xmpp_errors:bad_request()}
377 end;
378 _ ->
379 1 {error, mongoose_xmpp_errors:bad_request()}
380 end,
381 54 {ok, mongoose_acc:set(hook, result, Res, Acc)};
382 process_iq_get(Acc, _, _) ->
383 8 {ok, Acc}.
384
385 process_lists_get(Acc, HostType, LUser, LServer, Active) ->
386 3 case mod_privacy_backend:get_list_names(HostType, LUser, LServer) of
387 {ok, {Default, ListNames}} ->
388 3 {result, [list_names_query(Active, Default, ListNames)]};
389 {error, not_found} ->
390
:-(
{result, [empty_list_names_query()]};
391 {error, Reason} ->
392
:-(
?LOG_ERROR(#{what => privacy_get_list_names_failed,
393
:-(
reason => Reason, acc => Acc}),
394
:-(
{error, mongoose_xmpp_errors:internal_server_error()}
395 end.
396
397 process_list_get(Acc, HostType, LUser, LServer, {value, Name}) ->
398 50 case mod_privacy_backend:get_privacy_list(HostType, LUser, LServer, Name) of
399 {ok, List} ->
400 48 LItems = lists:map(fun item_to_xml/1, List),
401 48 {result, [list_query_result(Name, LItems)]};
402 {error, not_found} ->
403 2 {error, mongoose_xmpp_errors:item_not_found()};
404 {error, Reason} ->
405
:-(
?LOG_ERROR(#{what => privacy_get_privacy_list_failed,
406
:-(
reason => Reason, acc => Acc}),
407
:-(
{error, mongoose_xmpp_errors:internal_server_error()}
408 end;
409 process_list_get(_Acc, _HostType, _LUser, _LServer, false) ->
410
:-(
{error, mongoose_xmpp_errors:bad_request(<<"en">>, <<"name attribute is missing">>)}.
411
412 -spec process_iq_set(Acc, Params, Extra) -> {ok, Acc} when
413 Acc :: mongoose_acc:t(),
414 Params :: #{from := jid:jid(), iq := jlib:iq()},
415 Extra :: gen_hook:extra().
416 process_iq_set(Acc,
417 #{from := From, iq := #iq{xmlns = ?NS_PRIVACY, sub_el = SubEl}},
418 #{host_type := HostType}) ->
419 111 #xmlel{children = Els} = SubEl,
420 111 Res = case xml:remove_cdata(Els) of
421 [#xmlel{name = Name, attrs = Attrs, children = SubEls}] ->
422 111 ListName = xml:get_attr(<<"name">>, Attrs),
423 111 case Name of
424 <<"list">> ->
425 51 process_list_set(Acc, HostType, From, ListName,
426 xml:remove_cdata(SubEls));
427 <<"active">> ->
428 44 process_active_set(Acc, HostType, From, ListName);
429 <<"default">> ->
430 16 process_default_set(Acc, HostType, From, ListName);
431 _ ->
432
:-(
{error, mongoose_xmpp_errors:bad_request()}
433 end;
434 _ ->
435
:-(
{error, mongoose_xmpp_errors:bad_request()}
436 end,
437 111 {ok, mongoose_acc:set(hook, result, Res, Acc)};
438 process_iq_set(Acc, _, _) ->
439 28 {ok, Acc}.
440
441 process_default_set(Acc, HostType, #jid{luser = LUser, lserver = LServer}, {value, Name}) ->
442 15 case mod_privacy_backend:set_default_list(HostType, LUser, LServer, Name) of
443 ok ->
444 14 {result, []};
445 {error, not_found} ->
446 1 {error, mongoose_xmpp_errors:item_not_found()};
447 {error, Reason} ->
448
:-(
?LOG_ERROR(#{what => privacy_process_default_set_failed,
449
:-(
reason => Reason, acc => Acc}),
450
:-(
{error, mongoose_xmpp_errors:internal_server_error()}
451 end;
452 process_default_set(Acc, HostType, #jid{luser = LUser, lserver = LServer}, false) ->
453 1 case mod_privacy_backend:forget_default_list(HostType, LUser, LServer) of
454 ok ->
455 1 {result, []};
456 {error, Reason} ->
457
:-(
?LOG_ERROR(#{what => privacy_process_default_set_failed,
458
:-(
reason => Reason, acc => Acc}),
459
:-(
{error, mongoose_xmpp_errors:internal_server_error()}
460 end.
461
462 process_active_set(Acc, HostType, #jid{luser = LUser, lserver = LServer}, {value, Name}) ->
463 43 case mod_privacy_backend:get_privacy_list(HostType, LUser, LServer, Name) of
464 {ok, List} ->
465 42 NeedDb = is_list_needdb(List),
466 42 {result, [], #userlist{name = Name, list = List, needdb = NeedDb}};
467 {error, not_found} ->
468 1 {error, mongoose_xmpp_errors:item_not_found()};
469 {error, Reason} ->
470
:-(
?LOG_ERROR(#{what => privacy_process_active_set_failed,
471
:-(
reason => Reason, acc => Acc}),
472
:-(
{error, mongoose_xmpp_errors:internal_server_error()}
473 end;
474 process_active_set(_Acc, _HostType, _UserJID, false) ->
475 1 {result, [], #userlist{}}.
476
477 process_list_set(Acc, HostType, UserJID, {value, Name}, Els) ->
478 51 case parse_items(Els) of
479 false ->
480
:-(
{error, mongoose_xmpp_errors:bad_request()};
481 remove ->
482 1 remove_privacy_list(Acc, HostType, UserJID, Name);
483 List ->
484 50 replace_privacy_list(Acc, HostType, UserJID, Name, List)
485 end;
486 process_list_set(_Acc, _HostType, _UserJID, false, _Els) ->
487
:-(
{error, mongoose_xmpp_errors:bad_request()}.
488
489 remove_privacy_list(Acc, HostType, #jid{luser = LUser, lserver = LServer} = UserJID, Name) ->
490 1 case mod_privacy_backend:remove_privacy_list(HostType, LUser, LServer, Name) of
491 ok ->
492 1 UserList = #userlist{name = Name, list = []},
493 1 broadcast_privacy_list(HostType, UserJID, Name, UserList),
494 1 {result, []};
495 %% TODO if Name == Active -> conflict
496 {error, conflict} ->
497
:-(
{error, mongoose_xmpp_errors:conflict()};
498 {error, Reason} ->
499
:-(
?LOG_ERROR(#{what => privacy_remove_privacy_list_failed,
500
:-(
reason => Reason, acc => Acc}),
501
:-(
{error, mongoose_xmpp_errors:internal_server_error()}
502 end.
503
504 replace_privacy_list(Acc, HostType, #jid{luser = LUser, lserver = LServer} = UserJID, Name, List) ->
505 50 case mod_privacy_backend:replace_privacy_list(HostType, LUser, LServer, Name, List) of
506 ok ->
507 50 NeedDb = is_list_needdb(List),
508 50 UserList = #userlist{name = Name, list = List, needdb = NeedDb},
509 50 broadcast_privacy_list(HostType, UserJID, Name, UserList),
510 50 {result, []};
511 {error, Reason} ->
512
:-(
?LOG_ERROR(#{what => privacy_replace_privacy_list_failed,
513
:-(
reason => Reason, acc => Acc}),
514
:-(
{error, mongoose_xmpp_errors:internal_server_error()}
515 end.
516
517 is_list_needdb(Items) ->
518 162 lists:any(fun is_item_needdb/1, Items).
519
520 12 is_item_needdb(#listitem{type = subscription}) -> true;
521 2 is_item_needdb(#listitem{type = group}) -> true;
522 152 is_item_needdb(_) -> false.
523
524 -spec get_user_list(Acc, Params, Extra) -> {ok, Acc} when
525 Acc :: mongoose_privacy:userlist(),
526 Params :: #{jid := jid:jid()},
527 Extra :: gen_hook:extra().
528 get_user_list(_,
529 #{jid := #jid{luser = LUser, lserver = LServer}},
530 #{host_type := HostType}) ->
531 2524 UserList = case mod_privacy_backend:get_default_list(HostType, LUser, LServer) of
532 {ok, {Default, List}} ->
533 70 NeedDb = is_list_needdb(List),
534 70 #userlist{name = Default, list = List, needdb = NeedDb};
535 {error, _} ->
536 2454 #userlist{}
537 end,
538 2524 {ok, UserList}.
539
540 %% From is the sender, To is the destination.
541 %% If Dir = out, User@Server is the sender account (From).
542 %% If Dir = in, User@Server is the destination account (To).
543 -spec check_packet(Acc, Params, Extra) -> {ok, Acc} when
544 Acc :: mongoose_acc:t(),
545 Params :: #{jid := jid:jid(),
546 privacy_list := mongoose_privacy:userlist(),
547 from_to_name_type := {jid:jid(), jid:jid(), binary(), binary()},
548 dir := in | out},
549 Extra :: gen_hook:extra().
550 check_packet(Acc, #{privacy_list := #userlist{list = []}}, _) ->
551 2432 {ok, mongoose_acc:set(hook, result, allow, Acc)};
552 check_packet(Acc,
553 #{jid := JID,
554 privacy_list := #userlist{list = List, needdb = NeedDb},
555 from_to_name_type := {From, To, Name, Type},
556 dir := Dir},
557 #{host_type := HostType}) ->
558 255 PType = packet_directed_type(Dir, packet_type(Name, Type)),
559 255 LJID = case Dir of
560 200 in -> jid:to_lower(From);
561 55 out -> jid:to_lower(To)
562 end,
563 255 {Subscription, Groups} =
564 case NeedDb of
565 true ->
566 49 roster_get_jid_info(HostType, JID, LJID);
567 false ->
568 206 {[], []}
569 end,
570 255 CheckResult = check_packet_aux(List, PType, Type, LJID, Subscription, Groups),
571 255 {ok, mongoose_acc:set(hook, result, CheckResult, Acc)}.
572
573 %% allow error messages
574 check_packet_aux(_, message, <<"error">>, _JID, _Subscription, _Groups) ->
575 8 allow;
576 %% if we run of of list items then it is allowed
577 check_packet_aux([], _PType, _MType, _JID, _Subscription, _Groups) ->
578 181 allow;
579 %% check packet against next privacy list item
580 check_packet_aux([Item | List], PType, MType, JID, Subscription, Groups) ->
581 331 #listitem{type = Type, value = Value, action = Action} = Item,
582 331 do_check_packet_aux(Type, Action, PType, Value, JID, MType, Subscription, Groups, Item, List).
583
584 %% list set by blocking commands (XEP-0191) block all communication, both in and out,
585 %% for a given JID
586 do_check_packet_aux(jid, block, message, JID, JID, _, _, _, _, _) ->
587
:-(
block;
588 do_check_packet_aux(jid, block, message_out, JID, JID, _, _, _, _, _) ->
589 3 block;
590 %% then we do more complicated checking
591 do_check_packet_aux(Type, Action, PType, Value, JID, MType, Subscription, Groups, Item, List) ->
592 328 #listitem{type = Type, value = Value, action = Action} = Item,
593 328 case {is_ptype_match(Item, PType), Type} of
594 {true, none} ->
595 29 Action;
596 {true, _} ->
597 184 case is_type_match(Type, Value, JID, Subscription, Groups) of
598 true ->
599 34 Action;
600 false ->
601 150 check_packet_aux(List, PType, MType, JID, Subscription, Groups)
602 end;
603 {false, _} ->
604 115 check_packet_aux(List, PType, MType, JID, Subscription, Groups)
605 end.
606
607 is_ptype_match(#listitem{match_all = true}, _PType) ->
608 163 true;
609 is_ptype_match(Item, message) ->
610 41 Item#listitem.match_message;
611 is_ptype_match(_Item, message_out) ->
612 17 false; % according to xep-0016, privacy lists do not stop outgoing messages (so they say)
613 is_ptype_match(Item, iq) ->
614 74 Item#listitem.match_iq;
615 is_ptype_match(Item, presence_in) ->
616 12 Item#listitem.match_presence_in;
617 is_ptype_match(Item, presence_out) ->
618 5 Item#listitem.match_presence_out;
619 is_ptype_match(_Item, other) ->
620 16 false.
621
622 is_type_match(jid, Value, JID, _Subscription, _Groups) ->
623 174 case Value of
624 {<<>>, Server, <<>>} ->
625 6 case JID of
626 {_, Server, _} ->
627 6 true;
628 _ ->
629
:-(
false
630 end;
631 {User, Server, <<>>} ->
632 168 case JID of
633 {User, Server, _} ->
634 22 true;
635 _ ->
636 146 false
637 end;
638 _ ->
639
:-(
Value == JID
640 end;
641
642 is_type_match(subscription, Value, _JID, Subscription, _Groups) ->
643 9 Value == Subscription;
644 is_type_match(group, Value, _JID, _Subscription, Groups) ->
645 1 lists:member(Value, Groups).
646
647 -spec remove_user(Acc, Params, Extra) -> {ok, Acc} when
648 Acc :: mongoose_acc:t(),
649 Params :: #{jid := jid:jid()},
650 Extra :: #{host_type := mongooseim:host_type()}.
651 remove_user(Acc, #{jid := #jid{luser = LUser, lserver = LServer}}, #{host_type := HostType}) ->
652 114 R = mod_privacy_backend:remove_user(HostType, LUser, LServer),
653 114 mongoose_lib:log_if_backend_error(R, ?MODULE, ?LINE, {Acc, LUser, LServer}),
654 114 {ok, Acc}.
655
656 -spec remove_domain(Acc, Params, Extra) -> {ok, Acc} when
657 Acc :: mongoose_hooks:simple_acc(),
658 Params :: #{domain := jid:lserver()},
659 Extra :: gen_hook:extra().
660 remove_domain(Acc, #{domain := Domain}, #{host_type := HostType}) ->
661
:-(
mod_privacy_backend:remove_domain(HostType, Domain),
662
:-(
{ok, Acc}.
663
664 -spec updated_list(Acc, Params, Extra) -> {ok, Acc} when
665 Acc :: false | mongoose_privacy:userlist(),
666 Params :: #{old_list := mongoose_privacy:userlist(),
667 new_list := mongoose_privacy:userlist()},
668 Extra :: gen_hook:extra().
669 updated_list(_, #{old_list := #userlist{name = SameName},
670 new_list := #userlist{name = SameName} = NewList},
671 1 _) -> {ok, NewList};
672 52 updated_list(_, #{old_list := OldList}, _) -> {ok, OldList}.
673
674 %% ------------------------------------------------------------------
675 %% Deserialization
676 %% ------------------------------------------------------------------
677
678 16 packet_directed_type(out, message) -> message_out;
679 58 packet_directed_type(in, message) -> message;
680 120 packet_directed_type(in, iq) -> iq;
681 18 packet_directed_type(in, presence) -> presence_in;
682 14 packet_directed_type(out, presence) -> presence_out;
683 29 packet_directed_type(_Dir, _Type) -> other.
684
685 74 packet_type(<<"message">>, _Type) -> message;
686 141 packet_type(<<"iq">>, _Type) -> iq;
687 packet_type(<<"presence">>, Type) ->
688 40 case Type of
689 %% notification
690 20 undefined -> presence;
691 12 <<"unavailable">> -> presence;
692 %% subscribe, subscribed, unsubscribe,
693 %% unsubscribed, error, probe, or other
694 8 _ -> other
695 end.
696
697 parse_items([]) ->
698 1 remove;
699 parse_items(Els) ->
700 50 parse_items(Els, []).
701
702 parse_items([], Res) ->
703 %% Sort the items by their 'order' attribute
704 50 lists:keysort(#listitem.order, Res);
705 parse_items([#xmlel{name = <<"item">>, attrs = Attrs,
706 children = SubEls} | Els], Res) ->
707 57 Type = xml:get_attr_s(<<"type">>, Attrs),
708 57 Value = xml:get_attr_s(<<"value">>, Attrs),
709 57 SAction = xml:get_attr_s(<<"action">>, Attrs),
710 57 SOrder = xml:get_attr_s(<<"order">>, Attrs),
711 57 Action = parse_action(SAction),
712 57 Order = parse_order(SOrder),
713 57 I1 = set_action_and_order(Action, Order),
714 57 I2 = set_type_and_value(Type, Value, I1),
715 57 I3 = set_matches(SubEls, I2),
716 57 parse_items(Els, add_item(I3, Res));
717 parse_items(_, _Res) ->
718
:-(
false.
719
720 parse_action(<<>>) ->
721
:-(
false;
722 parse_action(Action) ->
723 57 binary_to_action_s(Action).
724
725 parse_order(<<>>) ->
726
:-(
false;
727 parse_order(Order) ->
728 57 validate_order(binary_to_order_s(Order)).
729
730 validate_order(Order) when Order >= 0 ->
731 57 Order;
732 validate_order(_) ->
733
:-(
false.
734
735 set_action_and_order(false, _) ->
736
:-(
false;
737 set_action_and_order(_, false) ->
738
:-(
false;
739 set_action_and_order(Action, Order) when is_atom(Action), is_integer(Order) ->
740 57 #listitem{action = Action, order = Order}.
741
742 set_type_and_value(_Type, _Value, false) ->
743
:-(
false;
744 set_type_and_value(<<>>, _Value, Item) ->
745 29 Item;
746 set_type_and_value(_Type, <<>>, _Item) ->
747
:-(
false;
748 set_type_and_value(<<"jid">>, Value, Item) ->
749 19 case jid:from_binary(Value) of
750 error ->
751
:-(
false;
752 JID ->
753 19 Item#listitem{type = jid, value = jid:to_lower(JID)}
754 end;
755 set_type_and_value(<<"group">>, Value, Item) ->
756 1 Item#listitem{type = group, value = Value};
757 set_type_and_value(<<"subscription">>, Value, Item) ->
758 8 case Value of
759 <<"none">> ->
760 2 Item#listitem{type = subscription, value = none};
761 <<"both">> ->
762 2 Item#listitem{type = subscription, value = both};
763 <<"from">> ->
764 2 Item#listitem{type = subscription, value = from};
765 <<"to">> ->
766 2 Item#listitem{type = subscription, value = to};
767 _ ->
768
:-(
false
769 end.
770
771 set_matches(_SubEls, false) ->
772
:-(
false;
773 set_matches(SubEls, Item) ->
774 57 parse_matches(Item, xml:remove_cdata(SubEls)).
775
776 parse_matches(Item, []) ->
777 12 Item#listitem{match_all = true};
778 parse_matches(Item, Els) ->
779 45 parse_matches1(Item, Els).
780
781 parse_matches1(Item, []) ->
782 45 Item;
783 parse_matches1(Item, [#xmlel{name = <<"message">>} | Els]) ->
784 37 parse_matches1(Item#listitem{match_message = true}, Els);
785 parse_matches1(Item, [#xmlel{name = <<"iq">>} | Els]) ->
786 1 parse_matches1(Item#listitem{match_iq = true}, Els);
787 parse_matches1(Item, [#xmlel{name = <<"presence-in">>} | Els]) ->
788 1 parse_matches1(Item#listitem{match_presence_in = true}, Els);
789 parse_matches1(Item, [#xmlel{name = <<"presence-out">>} | Els]) ->
790 6 parse_matches1(Item#listitem{match_presence_out = true}, Els);
791 parse_matches1(_Item, [#xmlel{} | _Els]) ->
792
:-(
false.
793
794 add_item(false, Items) ->
795
:-(
Items;
796 add_item(Item, Items) ->
797 57 [Item | Items].
798
799 %% ------------------------------------------------------------------
800 %% Serialization
801 %% ------------------------------------------------------------------
802
803 empty_list_names_query() ->
804
:-(
#xmlel{
805 name = <<"query">>,
806 attrs = [{<<"xmlns">>, ?NS_PRIVACY}]}.
807
808 list_names_query(Active, Default, ListNames) ->
809 3 #xmlel{
810 name = <<"query">>,
811 attrs = [{<<"xmlns">>, ?NS_PRIVACY}],
812 children = list_names(Active, Default, ListNames)}.
813
814 list_names(Active, Default, ListNames) ->
815 3 [list_name(<<"active">>, Active) || Active =/= none] ++
816 1 [list_name(<<"default">>, Default) || Default =/= none] ++
817 3 [list_name(<<"list">>, ListName) || ListName <- ListNames].
818
819 list_name(Type, Name) ->
820 5 #xmlel{name = Type, attrs = [{<<"name">>, Name}]}.
821
822 list_query_result(Name, LItems) ->
823 48 #xmlel{
824 name = <<"query">>,
825 attrs = [{<<"xmlns">>, ?NS_PRIVACY}],
826 children = [
827 #xmlel{
828 name = <<"list">>,
829 attrs = [{<<"name">>, Name}],
830 children = LItems}]}.
831
832 item_to_xml(Item) ->
833 55 #xmlel{
834 name = <<"item">>,
835 attrs = item_to_xml_attrs(Item),
836 children = item_to_xml_children(Item)}.
837
838 item_to_xml_attrs(Item=#listitem{type=none}) ->
839 29 item_to_xml_attrs1(Item);
840 item_to_xml_attrs(Item=#listitem{type=Type, value=Value}) ->
841 26 [{<<"type">>, type_to_binary(Type)},
842 {<<"value">>, value_to_binary(Type, Value)}
843 | item_to_xml_attrs1(Item)].
844
845 item_to_xml_attrs1(#listitem{action=Action, order=Order}) ->
846 55 [{<<"action">>, action_to_binary(Action)},
847 {<<"order">>, order_to_binary(Order)}].
848
849 item_to_xml_children(#listitem{match_all=true}) ->
850 10 [];
851 item_to_xml_children(#listitem{match_all=false,
852 match_iq=MatchIQ,
853 match_message=MatchMessage,
854 match_presence_in=MatchPresenceIn,
855 match_presence_out=MatchPresenceOut}) ->
856 37 [#xmlel{name = <<"message">>} || MatchMessage]
857 45 ++ [#xmlel{name = <<"presence-in">>} || MatchPresenceIn]
858 6 ++ [#xmlel{name = <<"presence-out">>} || MatchPresenceOut]
859 1 ++ [#xmlel{name = <<"iq">>} || MatchIQ].
860
861 action_to_binary(Action) ->
862 55 case Action of
863 5 allow -> <<"allow">>;
864 50 deny -> <<"deny">>
865 end.
866
867 order_to_binary(Order) ->
868 55 integer_to_binary(Order).
869
870 binary_to_order(Binary) ->
871 57 binary_to_integer(Binary).
872
873 type_to_binary(Type) ->
874 26 case Type of
875 17 jid -> <<"jid">>;
876 1 group -> <<"group">>;
877 8 subscription -> <<"subscription">>
878 end.
879
880 value_to_binary(Type, Val) ->
881 26 case Type of
882 17 jid -> jid:to_binary(Val);
883 1 group -> Val;
884 subscription ->
885 8 case Val of
886 2 both -> <<"both">>;
887 2 to -> <<"to">>;
888 2 from -> <<"from">>;
889 2 none -> <<"none">>
890 end
891 end.
892
893 binary_to_action(S) ->
894 57 case S of
895 5 <<"allow">> -> allow;
896 52 <<"deny">> -> deny
897 end.
898
899 binary_to_action_s(Action) ->
900 57 try
901 57 binary_to_action(Action)
902 catch error:_ ->
903
:-(
false
904 end.
905
906 binary_to_order_s(Order) ->
907 57 try
908 57 binary_to_order(Order)
909 catch error:_ ->
910
:-(
false
911 end.
912
913 %% ------------------------------------------------------------------
914 %% Ejabberd
915 %% ------------------------------------------------------------------
916
917 broadcast_privacy_list(HostType, #jid{luser = LUser, lserver = LServer}, Name, UserList) ->
918 51 Item = {privacy_list, UserList, Name},
919 51 UserPids = ejabberd_sm:get_user_present_pids(LUser, LServer),
920 51 mongoose_hooks:privacy_list_push(HostType, LUser, LServer, Item, length(UserPids)),
921 51 lists:foreach(fun({_, Pid}) -> mongoose_c2s:cast(Pid, ?MODULE, Item) end, UserPids).
922
923 roster_get_jid_info(HostType, ToJID, LJID) ->
924 49 mongoose_hooks:roster_get_jid_info(HostType, ToJID, LJID).
925
926 -spec config_metrics(mongooseim:host_type()) -> [{gen_mod:opt_key(), gen_mod:opt_value()}].
927 config_metrics(HostType) ->
928 24 mongoose_module_metrics:opts_for_module(HostType, ?MODULE, [backend]).
Line Hits Source