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