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 |
212 |
#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 |
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 |
1007 |
case mongoose_acc:stanza_type(Acc) of |
150 |
2 |
<<"error">> -> {ok, Acc}; |
151 |
469 |
<<"probe">> -> {ok, Acc}; |
152 |
536 |
_ -> 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 |
1459 |
case s_privacy_check_packet(Acc, StateData, in) of |
196 |
|
{allow, Acc1} -> |
197 |
1417 |
{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 |
2614 |
To = mongoose_acc:to_jid(Acc), |
311 |
2614 |
Jid = mongoose_c2s:get_jid(StateData), |
312 |
2614 |
PrivList = get_handler(StateData), |
313 |
2614 |
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 |
2868 |
case mongoose_c2s:get_mod_state(StateData, ?MODULE) of |
330 |
2465 |
{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 |
2465 |
Jid = mongoose_c2s:get_jid(StateData), |
337 |
2465 |
HostType = mongoose_c2s:get_host_type(StateData), |
338 |
2465 |
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 |
2519 |
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 |
2449 |
#userlist{} |
537 |
|
end, |
538 |
2519 |
{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 |
2427 |
{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]). |