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 |
168 |
#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 |
266 |
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 |
1010 |
case mongoose_acc:stanza_type(Acc) of |
150 |
2 |
<<"error">> -> {ok, Acc}; |
151 |
469 |
<<"probe">> -> {ok, Acc}; |
152 |
:-( |
<<"invisible">> -> {ok, Acc}; |
153 |
539 |
_ -> 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 |
1462 |
case s_privacy_check_packet(Acc, StateData, in) of |
197 |
|
{allow, Acc1} -> |
198 |
1421 |
{ok, Acc1}; |
199 |
|
{_, Acc1} -> |
200 |
41 |
{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 |
2617 |
To = mongoose_acc:to_jid(Acc), |
312 |
2617 |
Jid = mongoose_c2s:get_jid(StateData), |
313 |
2617 |
PrivList = get_handler(StateData), |
314 |
2617 |
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 |
7 |
Acc. |
327 |
|
|
328 |
|
-spec get_handler(mongoose_c2s:data()) -> mongoose_privacy:userlist() | {error, not_found}. |
329 |
|
get_handler(StateData) -> |
330 |
2871 |
case mongoose_c2s:get_mod_state(StateData, ?MODULE) of |
331 |
2469 |
{error, not_found} -> get_privacy_list(StateData); |
332 |
402 |
{ok, Handler} -> Handler |
333 |
|
end. |
334 |
|
|
335 |
|
-spec get_privacy_list(mongoose_c2s:data()) -> mongoose_privacy:userlist(). |
336 |
|
get_privacy_list(StateData) -> |
337 |
2469 |
Jid = mongoose_c2s:get_jid(StateData), |
338 |
2469 |
HostType = mongoose_c2s:get_host_type(StateData), |
339 |
2469 |
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 |
2523 |
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 |
2453 |
#userlist{} |
538 |
|
end, |
539 |
2523 |
{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 |
2431 |
{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 |
254 |
PType = packet_directed_type(Dir, packet_type(Name, Type)), |
560 |
254 |
LJID = case Dir of |
561 |
199 |
in -> jid:to_lower(From); |
562 |
55 |
out -> jid:to_lower(To) |
563 |
|
end, |
564 |
254 |
{Subscription, Groups} = |
565 |
|
case NeedDb of |
566 |
|
true -> |
567 |
49 |
roster_get_jid_info(HostType, JID, LJID); |
568 |
|
false -> |
569 |
205 |
{[], []} |
570 |
|
end, |
571 |
254 |
CheckResult = check_packet_aux(List, PType, Type, LJID, Subscription, Groups), |
572 |
254 |
{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 |
330 |
#listitem{type = Type, value = Value, action = Action} = Item, |
583 |
330 |
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 |
327 |
#listitem{type = Type, value = Value, action = Action} = Item, |
594 |
327 |
case {is_ptype_match(Item, PType), Type} of |
595 |
|
{true, none} -> |
596 |
29 |
Action; |
597 |
|
{true, _} -> |
598 |
183 |
case is_type_match(Type, Value, JID, Subscription, Groups) of |
599 |
|
true -> |
600 |
33 |
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 |
162 |
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 |
173 |
case Value of |
625 |
|
{<<>>, Server, <<>>} -> |
626 |
6 |
case JID of |
627 |
|
{_, Server, _} -> |
628 |
6 |
true; |
629 |
|
_ -> |
630 |
:-( |
false |
631 |
|
end; |
632 |
|
{User, Server, <<>>} -> |
633 |
167 |
case JID of |
634 |
|
{User, Server, _} -> |
635 |
21 |
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 |
17 |
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 |
39 |
case Type of |
690 |
|
%% notification |
691 |
20 |
undefined -> presence; |
692 |
11 |
<<"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]). |