1 |
|
%%%---------------------------------------------------------------------- |
2 |
|
%%% File : mod_muc_light.erl |
3 |
|
%%% Author : Piotr Nosek <piotr.nosek@erlang-solutions.com> |
4 |
|
%%% Purpose : MUC light support |
5 |
|
%%% Created : 8 Sep 2014 by Piotr Nosek <piotr.nosek@erlang-solutions.com> |
6 |
|
%%% |
7 |
|
%%% Looking for documentation excerpt that was present here for 5 years? |
8 |
|
%%% Now everything is moved to doc/modules/mod_muc_light.md |
9 |
|
%%% |
10 |
|
%%%---------------------------------------------------------------------- |
11 |
|
|
12 |
|
-module(mod_muc_light). |
13 |
|
-author('piotr.nosek@erlang-solutions.com'). |
14 |
|
|
15 |
|
-include("mongoose.hrl"). |
16 |
|
-include("jlib.hrl"). |
17 |
|
-include("mod_muc_light.hrl"). |
18 |
|
-include("mod_roster.hrl"). |
19 |
|
-include("mongoose_rsm.hrl"). |
20 |
|
-include("mongoose_config_spec.hrl"). |
21 |
|
|
22 |
|
-behaviour(gen_mod). |
23 |
|
-behaviour(mongoose_packet_handler). |
24 |
|
-behaviour(mongoose_module_metrics). |
25 |
|
|
26 |
|
%% API |
27 |
|
-export([server_host_to_muc_host/2]). |
28 |
|
-export([config_schema/1]). |
29 |
|
|
30 |
|
%% For Administration API |
31 |
|
-export([try_to_create_room/3, |
32 |
|
change_room_config/5, |
33 |
|
delete_room/1]). |
34 |
|
|
35 |
|
%% gen_mod callbacks |
36 |
|
-export([start/2, stop/1, hooks/1, config_spec/0, supported_features/0, deps/2]). |
37 |
|
|
38 |
|
%% config processing callback |
39 |
|
-export([process_config_schema/1]). |
40 |
|
|
41 |
|
%% Packet handler export |
42 |
|
-export([process_packet/5]). |
43 |
|
|
44 |
|
%% Hook handlers |
45 |
|
-export([prevent_service_unavailable/3, |
46 |
|
disco_local_items/3, |
47 |
|
remove_user/3, |
48 |
|
remove_domain/3, |
49 |
|
add_rooms_to_roster/3, |
50 |
|
process_iq_get/3, |
51 |
|
process_iq_set/3, |
52 |
|
is_muc_room_owner/3, |
53 |
|
can_access_room/3, |
54 |
|
acc_room_affiliations/3, |
55 |
|
room_exists/3, |
56 |
|
can_access_identity/3]). |
57 |
|
|
58 |
|
-export([get_acc_room_affiliations/2]). |
59 |
|
|
60 |
|
%% For propEr |
61 |
|
-export([apply_rsm/3]). |
62 |
|
|
63 |
|
-export([config_metrics/1]). |
64 |
|
%% for mod_muc_light_codec_legacy |
65 |
|
-export([subdomain_pattern/1]). |
66 |
|
|
67 |
|
-export([get_room_affs_from_acc/2, set_room_affs_from_acc/3]). |
68 |
|
|
69 |
|
%% For tests |
70 |
|
-export([default_schema/0, |
71 |
|
force_clear_from_ct/1]). |
72 |
|
|
73 |
|
-export_type([aff_users/0]). |
74 |
|
|
75 |
|
-ignore_xref([ |
76 |
|
apply_rsm/3, default_schema/0, force_clear_from_ct/1, server_host_to_muc_host/2 |
77 |
|
]). |
78 |
|
|
79 |
|
-type muc_server() :: jid:lserver(). |
80 |
|
-type host_type() :: mongooseim:host_type(). |
81 |
|
-type versioned_affs() :: {ok, aff_users(), binary()}. |
82 |
|
|
83 |
|
%%==================================================================== |
84 |
|
%% API |
85 |
|
%%==================================================================== |
86 |
|
|
87 |
|
-spec supported_features() -> [atom()]. |
88 |
|
supported_features() -> |
89 |
:-( |
[dynamic_domains]. |
90 |
|
|
91 |
|
-spec default_schema() -> mod_muc_light_room_config:schema(). |
92 |
|
default_schema() -> |
93 |
|
% This list needs to be sorted |
94 |
206 |
[{<<"roomname">>, <<"Untitled">>, roomname, binary}, |
95 |
|
{<<"subject">>, <<>>, subject, binary}]. |
96 |
|
|
97 |
|
-spec default_host() -> mongoose_subdomain_utils:subdomain_pattern(). |
98 |
|
default_host() -> |
99 |
202 |
mongoose_subdomain_utils:make_subdomain_pattern(<<"muclight.@HOST@">>). |
100 |
|
|
101 |
|
-spec config_schema(MUCServer :: muc_server()) -> mod_muc_light_room_config:schema(). |
102 |
|
config_schema(MUCServer) -> |
103 |
149 |
HostType = mod_muc_light_utils:muc_host_to_host_type(MUCServer), |
104 |
149 |
config_schema_for_host_type(HostType). |
105 |
|
|
106 |
|
%% Internals |
107 |
|
|
108 |
|
-spec config_schema_for_host_type(host_type()) -> mod_muc_light_room_config:schema(). |
109 |
|
config_schema_for_host_type(HostType) -> |
110 |
285 |
gen_mod:get_module_opt(HostType, ?MODULE, config_schema). |
111 |
|
|
112 |
|
force_clear_from_ct(HostType) -> |
113 |
83 |
catch mod_muc_light_cache:force_clear(HostType), |
114 |
83 |
mod_muc_light_db_backend:force_clear(HostType). |
115 |
|
|
116 |
|
%%==================================================================== |
117 |
|
%% Administration API |
118 |
|
%%==================================================================== |
119 |
|
|
120 |
|
-spec try_to_create_room(CreatorJid :: jid:jid(), RoomJID :: jid:jid(), |
121 |
|
CreationCfg :: create_req_props()) -> |
122 |
|
{ok, jid:jid(), create_req_props()} | validation_error() |
123 |
|
| {error, bad_request | exists | max_occupants_reached}. |
124 |
|
try_to_create_room(CreatorJid, RoomJID, #create{raw_config = RawConfig} = CreationCfg) -> |
125 |
136 |
RoomUS = jid:to_lus(RoomJID), |
126 |
136 |
HostType = mod_muc_light_utils:room_jid_to_host_type(RoomJID), |
127 |
136 |
CfgRes = prepare_config(HostType, RawConfig), |
128 |
136 |
AffRes = prepare_affs(HostType, CreatorJid, RoomUS, CreationCfg), |
129 |
136 |
case {CfgRes, AffRes} of |
130 |
|
{{ok, Config0}, {ok, FinalAffUsers}} -> |
131 |
134 |
Version = mongoose_bin:gen_from_timestamp(), |
132 |
134 |
case mod_muc_light_db_backend:create_room( |
133 |
|
HostType, RoomUS, lists:sort(Config0), FinalAffUsers, Version) of |
134 |
|
{ok, {FinalU, FinalS}} -> |
135 |
127 |
{ok, jid:make_noprep(FinalU, FinalS, <<>>), CreationCfg#create{ |
136 |
|
aff_users = FinalAffUsers, version = Version}}; |
137 |
|
Other -> |
138 |
7 |
Other |
139 |
|
end; |
140 |
|
{{error, _} = Error, _} -> |
141 |
1 |
Error; |
142 |
|
{_, {error, _} = Error} -> |
143 |
1 |
Error |
144 |
|
end. |
145 |
|
|
146 |
|
-spec change_room_config(UserJid :: jid:jid(), RoomID :: jid:user(), |
147 |
|
MUCLightDomain :: jid:server(), |
148 |
|
ConfigReq :: config_req_props(), |
149 |
|
Acc :: mongoose_acc:t()) -> |
150 |
|
{ok, jid:jid(), config_req_props()} |
151 |
|
| {error, validation_error() | bad_request | not_allowed | not_exists | item_not_found}. |
152 |
|
change_room_config(UserJid, RoomID, MUCLightDomain, ConfigReq, Acc1) -> |
153 |
20 |
RoomJID = jid:make_bare(RoomID, MUCLightDomain), |
154 |
20 |
{Acc2, AffUsersRes} = get_acc_room_affiliations(Acc1, RoomJID), |
155 |
20 |
case mod_muc_light_room:process_request(UserJid, RoomJID, {set, ConfigReq}, |
156 |
|
AffUsersRes, Acc2) of |
157 |
|
{set, ConfigResp, _} -> |
158 |
7 |
{ok, RoomJID, ConfigResp}; |
159 |
|
{error, _Reason} = E -> |
160 |
13 |
E |
161 |
|
end. |
162 |
|
|
163 |
|
-spec delete_room(RoomUS :: jid:simple_bare_jid()) -> ok | {error, not_exists}. |
164 |
|
delete_room({_, RoomS} = RoomUS) -> |
165 |
7 |
HostType = mod_muc_light_utils:muc_host_to_host_type(RoomS), |
166 |
7 |
mod_muc_light_db_backend:destroy_room(HostType, RoomUS). |
167 |
|
|
168 |
|
%%==================================================================== |
169 |
|
%% gen_mod callbacks |
170 |
|
%%==================================================================== |
171 |
|
|
172 |
|
-spec start(host_type(), gen_mod:module_opts()) -> ok. |
173 |
|
start(HostType, Opts) -> |
174 |
48 |
Codec = host_type_to_codec(HostType), |
175 |
48 |
mod_muc_light_db_backend:start(HostType, Opts), |
176 |
48 |
mod_muc_light_codec_backend:start(HostType, #{backend => Codec}), |
177 |
|
%% Handler |
178 |
48 |
SubdomainPattern = subdomain_pattern(HostType), |
179 |
48 |
PacketHandler = mongoose_packet_handler:new(?MODULE), |
180 |
48 |
mongoose_domain_api:register_subdomain(HostType, SubdomainPattern, PacketHandler), |
181 |
48 |
ok. |
182 |
|
|
183 |
|
-spec stop(host_type()) -> ok. |
184 |
|
stop(HostType) -> |
185 |
48 |
SubdomainPattern = subdomain_pattern(HostType), |
186 |
48 |
mongoose_domain_api:unregister_subdomain(HostType, SubdomainPattern), |
187 |
48 |
mod_muc_light_codec_backend:stop(HostType), |
188 |
48 |
mod_muc_light_db_backend:stop(HostType), |
189 |
48 |
ok. |
190 |
|
|
191 |
|
-spec deps(mongooseim:host_type(), gen_mod:module_opts()) -> gen_mod_deps:deps(). |
192 |
|
deps(_HostType, #{cache_affs := CacheOpts}) -> |
193 |
212 |
[{mod_muc_light_cache, CacheOpts, hard}]; |
194 |
|
deps(_HostType, #{}) -> |
195 |
220 |
[]. |
196 |
|
|
197 |
|
%% Init helpers |
198 |
|
subdomain_pattern(HostType) -> |
199 |
101 |
gen_mod:get_module_opt(HostType, ?MODULE, host). |
200 |
|
|
201 |
|
server_host_to_muc_host(HostType, ServerHost) -> |
202 |
2 |
mongoose_subdomain_utils:get_fqdn(subdomain_pattern(HostType), ServerHost). |
203 |
|
|
204 |
|
host_type_to_codec(HostType) -> |
205 |
144 |
case gen_mod:get_module_opt(HostType, ?MODULE, legacy_mode) of |
206 |
|
true -> |
207 |
27 |
legacy; |
208 |
|
false -> |
209 |
117 |
modern |
210 |
|
end. |
211 |
|
|
212 |
|
%% Config callbacks |
213 |
|
-spec config_spec() -> mongoose_config_spec:config_section(). |
214 |
|
config_spec() -> |
215 |
202 |
#section{ |
216 |
|
items = #{<<"backend">> => #option{type = atom, |
217 |
|
validate = {module, mod_muc_light_db}}, |
218 |
|
<<"cache_affs">> => mod_muc_light_cache:config_spec(), |
219 |
|
<<"host">> => #option{type = string, |
220 |
|
validate = subdomain_template, |
221 |
|
process = fun mongoose_subdomain_utils:make_subdomain_pattern/1}, |
222 |
|
<<"equal_occupants">> => #option{type = boolean}, |
223 |
|
<<"legacy_mode">> => #option{type = boolean}, |
224 |
|
<<"rooms_per_user">> => #option{type = int_or_infinity, |
225 |
|
validate = positive}, |
226 |
|
<<"blocking">> => #option{type = boolean}, |
227 |
|
<<"all_can_configure">> => #option{type = boolean}, |
228 |
|
<<"all_can_invite">> => #option{type = boolean}, |
229 |
|
<<"max_occupants">> => #option{type = int_or_infinity, |
230 |
|
validate = positive}, |
231 |
|
<<"rooms_per_page">> => #option{type = int_or_infinity, |
232 |
|
validate = positive}, |
233 |
|
<<"rooms_in_rosters">> => #option{type = boolean}, |
234 |
|
<<"config_schema">> => #list{items = config_schema_spec(), |
235 |
|
process = fun ?MODULE:process_config_schema/1} |
236 |
|
}, |
237 |
|
defaults = #{<<"backend">> => mnesia, |
238 |
|
<<"host">> => default_host(), |
239 |
|
<<"equal_occupants">> => ?DEFAULT_EQUAL_OCCUPANTS, |
240 |
|
<<"legacy_mode">> => ?DEFAULT_LEGACY_MODE, |
241 |
|
<<"rooms_per_user">> => ?DEFAULT_ROOMS_PER_USER, |
242 |
|
<<"blocking">> => ?DEFAULT_BLOCKING, |
243 |
|
<<"all_can_configure">> => ?DEFAULT_ALL_CAN_CONFIGURE, |
244 |
|
<<"all_can_invite">> => ?DEFAULT_ALL_CAN_INVITE, |
245 |
|
<<"max_occupants">> => ?DEFAULT_MAX_OCCUPANTS, |
246 |
|
<<"rooms_per_page">> => ?DEFAULT_ROOMS_PER_PAGE, |
247 |
|
<<"rooms_in_rosters">> => ?DEFAULT_ROOMS_IN_ROSTERS, |
248 |
|
<<"config_schema">> => default_schema()} |
249 |
|
}. |
250 |
|
|
251 |
|
config_schema_spec() -> |
252 |
202 |
#section{ |
253 |
|
items = #{<<"field">> => #option{type = binary, |
254 |
|
validate = non_empty}, |
255 |
|
<<"string_value">> => #option{type = binary}, |
256 |
|
<<"integer_value">> => #option{type = integer}, |
257 |
|
<<"float_value">> => #option{type = float}, |
258 |
|
<<"internal_key">> => #option{type = atom, |
259 |
|
validate = non_empty} |
260 |
|
}, |
261 |
|
required = [<<"field">>] |
262 |
|
}. |
263 |
|
|
264 |
|
-spec process_config_schema([map()]) -> mod_muc_light_room_config:schema(). |
265 |
|
process_config_schema(Items) -> |
266 |
:-( |
lists:ukeysort(1, lists:map(fun process_config_schema_item/1, Items)). |
267 |
|
|
268 |
|
process_config_schema_item(#{field := FieldName} = FieldSpec) -> |
269 |
:-( |
InternalKey = maps:get(internal_key, FieldSpec, binary_to_atom(FieldName)), |
270 |
:-( |
FieldTypes = schema_field_types(), |
271 |
:-( |
case [K || K <- maps:keys(FieldTypes), maps:is_key(K, FieldSpec)] of |
272 |
|
[Key] -> |
273 |
:-( |
{FieldName, maps:get(Key, FieldSpec), InternalKey, maps:get(Key, FieldTypes)}; |
274 |
|
_ -> |
275 |
:-( |
error(#{what => invalid_schema_field_specification, field_spec => FieldSpec}) |
276 |
|
end. |
277 |
|
|
278 |
|
schema_field_types() -> |
279 |
:-( |
#{string_value => binary, integer_value => integer, float_value => float}. |
280 |
|
|
281 |
|
-spec hooks(mongooseim:host_type()) -> gen_hook:hook_list(). |
282 |
|
hooks(HostType) -> |
283 |
96 |
Codec = host_type_to_codec(HostType), |
284 |
96 |
Roster = gen_mod:get_module_opt(HostType, ?MODULE, rooms_in_rosters), |
285 |
|
[{is_muc_room_owner, HostType, fun ?MODULE:is_muc_room_owner/3, #{}, 50}, |
286 |
|
{can_access_room, HostType, fun ?MODULE:can_access_room/3, #{}, 50}, |
287 |
|
{acc_room_affiliations, HostType, fun ?MODULE:acc_room_affiliations/3, #{}, 50}, |
288 |
|
{room_exists, HostType, fun ?MODULE:room_exists/3, #{}, 50}, |
289 |
|
{can_access_identity, HostType, fun ?MODULE:can_access_identity/3, #{}, 50}, |
290 |
|
%% Prevent sending service-unavailable on groupchat messages |
291 |
|
{offline_groupchat_message_hook, HostType, fun ?MODULE:prevent_service_unavailable/3, #{}, 90}, |
292 |
|
{remove_user, HostType, fun ?MODULE:remove_user/3, #{}, 50}, |
293 |
|
{remove_domain, HostType, fun ?MODULE:remove_domain/3, #{}, 50}, |
294 |
96 |
{disco_local_items, HostType, fun ?MODULE:disco_local_items/3, #{}, 50}] ++ |
295 |
|
case Codec of |
296 |
|
legacy -> |
297 |
18 |
[{privacy_iq_get, HostType, fun ?MODULE:process_iq_get/3, #{}, 1}, |
298 |
|
{privacy_iq_set, HostType, fun ?MODULE:process_iq_set/3, #{}, 1}]; |
299 |
|
_ -> |
300 |
78 |
[] |
301 |
|
end ++ |
302 |
|
case Roster of |
303 |
26 |
false -> []; |
304 |
70 |
true -> [{roster_get, HostType, fun ?MODULE:add_rooms_to_roster/3, #{}, 50}] |
305 |
|
end. |
306 |
|
|
307 |
|
%%==================================================================== |
308 |
|
%% Routing |
309 |
|
%%==================================================================== |
310 |
|
|
311 |
|
-spec process_packet(Acc :: mongoose_acc:t(), From ::jid:jid(), To ::jid:jid(), |
312 |
|
El :: exml:element(), Extra :: gen_hook:extra()) -> mongoose_acc:t(). |
313 |
|
process_packet(Acc, From, To, El, _Extra) -> |
314 |
282 |
HostType = mod_muc_light_utils:acc_to_host_type(Acc), |
315 |
282 |
DecodedPacket = mod_muc_light_codec_backend:decode(From, To, El, Acc), |
316 |
282 |
process_decoded_packet(HostType, From, To, Acc, El, DecodedPacket). |
317 |
|
|
318 |
|
-spec process_decoded_packet( |
319 |
|
HostType :: host_type(), |
320 |
|
From :: jid:jid(), To :: jid:jid(), |
321 |
|
Acc :: mongoose_acc:t(), |
322 |
|
OrigPacket :: exml:element(), |
323 |
|
DecodedPacket :: mod_muc_light_codec_backend:decode_result()) -> |
324 |
|
mongoose_acc:t(). |
325 |
|
process_decoded_packet(HostType, From, To, Acc, El, |
326 |
|
{ok, {set, #create{} = Create}}) -> |
327 |
39 |
case not mod_muc_light_utils:room_limit_reached(From, HostType) of |
328 |
38 |
true -> create_room(Acc, From, To, Create, El); |
329 |
1 |
false -> make_err(From, To, El, Acc, {error, room_limit_reached}) |
330 |
|
end; |
331 |
|
process_decoded_packet(_HostType, From, To, Acc, _El, |
332 |
|
{ok, {get, #disco_info{} = DI}}) -> |
333 |
4 |
handle_disco_info_get(From, To, DI, Acc); |
334 |
|
process_decoded_packet(HostType, From, To, Acc, El, |
335 |
|
{ok, {get, #disco_items{} = DI}}) -> |
336 |
21 |
handle_disco_items_get(HostType, Acc, From, To, DI, El); |
337 |
|
process_decoded_packet(HostType, From, To, Acc, El, |
338 |
|
{ok, {_, #blocking{}} = Blocking}) -> |
339 |
28 |
case gen_mod:get_module_opt(HostType, ?MODULE, blocking) of |
340 |
|
true -> |
341 |
24 |
case handle_blocking(Acc, From, To, Blocking) of |
342 |
:-( |
{error, _} = Res -> make_err(From, To, El, Acc, Res); |
343 |
24 |
_ -> Acc |
344 |
|
end; |
345 |
4 |
false -> make_err(From, To, El, Acc, {error, blocking_disabled}) |
346 |
|
end; |
347 |
|
process_decoded_packet(_HostType, From, To, Acc, El, |
348 |
|
{ok, #iq{} = IQ}) -> |
349 |
:-( |
case mod_muc_iq:process_iq(To#jid.lserver, From, To, Acc, IQ) of |
350 |
|
{Acc1, error} -> |
351 |
:-( |
E = {error, {feature_not_implemented, <<"mod_muc_iq returns error">>}}, |
352 |
:-( |
make_err(From, To, El, Acc1, E); |
353 |
:-( |
_ -> Acc |
354 |
|
end; |
355 |
|
process_decoded_packet(HostType, From, To, Acc, El, |
356 |
|
{ok, RequestToRoom}) |
357 |
|
when To#jid.luser =/= <<>> -> |
358 |
183 |
case mongoose_hooks:room_exists(HostType, To) of |
359 |
182 |
true -> mod_muc_light_room:handle_request(From, To, El, RequestToRoom, Acc); |
360 |
1 |
false -> make_err(From, To, El, Acc, {error, item_not_found}) |
361 |
|
end; |
362 |
|
process_decoded_packet(_HostType, From, To, Acc, El, |
363 |
|
{error, _} = Err) -> |
364 |
4 |
make_err(From, To, El, Acc, Err); |
365 |
|
process_decoded_packet(_HostType, _From, _To, Acc, _El, ignore) -> |
366 |
3 |
Acc; |
367 |
|
process_decoded_packet(_HostType, From, To, Acc, El, InvalidReq) -> |
368 |
:-( |
?LOG_WARNING(#{what => muc_light_invalid_request, |
369 |
:-( |
acc => Acc, reason => InvalidReq}), |
370 |
:-( |
make_err(From, To, El, Acc, {error, bad_request}). |
371 |
|
|
372 |
|
make_err(From, To, El, Acc, Reason) -> |
373 |
10 |
mod_muc_light_codec_backend:encode_error(Reason, From, To, El, Acc). |
374 |
|
|
375 |
|
%%==================================================================== |
376 |
|
%% Hook handlers |
377 |
|
%%==================================================================== |
378 |
|
|
379 |
|
-spec prevent_service_unavailable(Acc, Params, Extra) -> {ok | stop, Acc} when |
380 |
|
Acc :: mongoose_acc:t(), |
381 |
|
Params :: map(), |
382 |
|
Extra :: gen_hook:extra(). |
383 |
|
prevent_service_unavailable(Acc, #{packet := Packet}, _Extra) -> |
384 |
190 |
case xml:get_tag_attr_s(<<"type">>, Packet) of |
385 |
190 |
<<"groupchat">> -> {stop, Acc}; |
386 |
:-( |
_Type -> {ok, Acc} |
387 |
|
end. |
388 |
|
|
389 |
|
-spec disco_local_items(Acc, Params, Extra) -> {ok, Acc} when |
390 |
|
Acc :: mongoose_acc:t(), |
391 |
|
Params :: map(), |
392 |
|
Extra :: gen_hook:extra(). |
393 |
|
disco_local_items(Acc = #{host_type := HostType, |
394 |
|
to_jid := #jid{lserver = ServerHost}, |
395 |
|
node := <<>>}, |
396 |
|
_Params, |
397 |
|
_Extra) -> |
398 |
2 |
XMLNS = case legacy_mode(HostType) of |
399 |
1 |
true -> ?NS_MUC; |
400 |
1 |
false -> ?NS_MUC_LIGHT |
401 |
|
end, |
402 |
2 |
MUCHost = server_host_to_muc_host(HostType, ServerHost), |
403 |
2 |
Items = [#{jid => MUCHost, node => XMLNS}], |
404 |
2 |
{ok, mongoose_disco:add_items(Items, Acc)}; |
405 |
|
disco_local_items(Acc, _Params, _Extra) -> |
406 |
:-( |
{ok, Acc}. |
407 |
|
|
408 |
|
legacy_mode(HostType) -> |
409 |
2 |
gen_mod:get_module_opt(HostType, ?MODULE, legacy_mode). |
410 |
|
|
411 |
|
-spec remove_user(Acc, Params, Extra) -> {ok, Acc} when |
412 |
|
Acc :: mongoose_acc:t(), |
413 |
|
Params :: map(), |
414 |
|
Extra :: gen_hook:extra(). |
415 |
|
remove_user(Acc, #{jid := UserJid}, #{host_type := HostType}) -> |
416 |
209 |
Version = mongoose_bin:gen_from_timestamp(), |
417 |
209 |
case mod_muc_light_db_backend:remove_user(HostType, jid:to_lus(UserJid), Version) of |
418 |
|
{error, Reason} -> |
419 |
:-( |
?LOG_ERROR(#{what => muc_remove_user_failed, |
420 |
:-( |
reason => Reason, acc => Acc}), |
421 |
:-( |
{ok, Acc}; |
422 |
|
AffectedRooms -> |
423 |
209 |
bcast_removed_user(Acc, UserJid, AffectedRooms, Version), |
424 |
209 |
maybe_forget_rooms(Acc, AffectedRooms, Version), |
425 |
209 |
{ok, Acc} |
426 |
|
end. |
427 |
|
|
428 |
|
-spec remove_domain(Acc, Params, Extra) -> {ok | stop, Acc} when |
429 |
|
Acc :: mongoose_domain_api:remove_domain_acc(), |
430 |
|
Params :: map(), |
431 |
|
Extra :: gen_hook:extra(). |
432 |
|
remove_domain(Acc, #{domain := Domain}, #{host_type := HostType}) -> |
433 |
:-( |
F = fun() -> |
434 |
:-( |
MUCHost = server_host_to_muc_host(HostType, Domain), |
435 |
:-( |
mod_muc_light_db_backend:remove_domain(HostType, MUCHost, Domain), |
436 |
:-( |
Acc |
437 |
|
end, |
438 |
:-( |
mongoose_domain_api:remove_domain_wrapper(Acc, F, ?MODULE). |
439 |
|
|
440 |
|
-spec add_rooms_to_roster(Acc, Params, Extra) -> {ok, Acc} when |
441 |
|
Acc :: mongoose_acc:t(), |
442 |
|
Params :: map(), |
443 |
|
Extra :: gen_hook:extra(). |
444 |
|
add_rooms_to_roster(Acc, #{jid := UserJID}, _Extra) -> |
445 |
16 |
Items = mongoose_acc:get(roster, items, [], Acc), |
446 |
16 |
HostType = mongoose_acc:host_type(Acc), |
447 |
16 |
RoomList = mod_muc_light_db_backend:get_user_rooms(HostType, jid:to_lus(UserJID), undefined), |
448 |
16 |
Info = get_rooms_info(HostType, lists:sort(RoomList)), |
449 |
16 |
NewItems = [make_roster_item(Item) || Item <- Info] ++ Items, |
450 |
16 |
{ok, mongoose_acc:set(roster, items, NewItems, Acc)}. |
451 |
|
|
452 |
|
make_roster_item({{RoomU, RoomS}, RoomName, RoomVersion}) -> |
453 |
5 |
JID = jid:make_noprep(RoomU, RoomS, <<>>), |
454 |
5 |
VerEl = #xmlel{ name = <<"version">>, |
455 |
|
children = [#xmlcdata{ content = RoomVersion }] }, |
456 |
5 |
#roster{usj = {RoomU, RoomS, jid:to_lower(JID)}, |
457 |
|
us = {RoomU, RoomS}, |
458 |
|
jid = jid:to_lower(JID), |
459 |
|
name = RoomName, |
460 |
|
subscription = to, |
461 |
|
groups = [?NS_MUC_LIGHT], |
462 |
|
xs = [VerEl] }. |
463 |
|
|
464 |
|
-spec process_iq_get(Acc, Params, Extra) -> {ok | stop, Acc} when |
465 |
|
Acc :: mongoose_acc:t(), |
466 |
|
Params :: map(), |
467 |
|
Extra :: gen_hook:extra(). |
468 |
|
process_iq_get(Acc, #{from := #jid{lserver = FromS} = From, to := To, iq := IQ}, #{host_type := HostType}) -> |
469 |
:-( |
MUCHost = server_host_to_muc_host(HostType, FromS), |
470 |
:-( |
case {mod_muc_light_codec_backend:decode(From, To, IQ, Acc), |
471 |
|
gen_mod:get_module_opt(HostType, ?MODULE, blocking)} of |
472 |
|
{{ok, {get, #blocking{} = Blocking}}, true} -> |
473 |
:-( |
Items = mod_muc_light_db_backend:get_blocking(HostType, jid:to_lus(From), MUCHost), |
474 |
:-( |
mod_muc_light_codec_backend:encode( |
475 |
|
{get, Blocking#blocking{ items = Items }}, From, To, |
476 |
:-( |
fun(_, _, Packet) -> put(encode_res, Packet) end, |
477 |
|
Acc), |
478 |
:-( |
#xmlel{ children = ResponseChildren } = erase(encode_res), |
479 |
:-( |
Result = {result, ResponseChildren}, |
480 |
:-( |
{stop, mongoose_acc:set(hook, result, Result, Acc)}; |
481 |
|
{{ok, {get, #blocking{}}}, false} -> |
482 |
:-( |
Result = {error, mongoose_xmpp_errors:bad_request()}, |
483 |
:-( |
{stop, mongoose_acc:set(hook, result, Result, Acc)}; |
484 |
|
_ -> |
485 |
:-( |
Result = {error, mongoose_xmpp_errors:bad_request()}, |
486 |
:-( |
{ok, mongoose_acc:set(hook, result, Result, Acc)} |
487 |
|
end. |
488 |
|
|
489 |
|
%% Blocking is done using your local domain |
490 |
|
-spec process_iq_set(Acc, Params, Extra) -> {ok | stop, Acc} when |
491 |
|
Acc :: mongoose_acc:t(), |
492 |
|
Params :: map(), |
493 |
|
Extra :: gen_hook:extra(). |
494 |
|
process_iq_set(Acc, #{from := #jid{ lserver = FromS } = From, to := To, iq := IQ}, _Extra) -> |
495 |
:-( |
HostType = mod_muc_light_utils:acc_to_host_type(Acc), |
496 |
:-( |
MUCHost = server_host_to_muc_host(HostType, FromS), |
497 |
:-( |
case {mod_muc_light_codec_backend:decode(From, To, IQ, Acc), |
498 |
|
gen_mod:get_module_opt(HostType, ?MODULE, blocking)} of |
499 |
|
{{ok, {set, #blocking{ items = Items }} = Blocking}, true} -> |
500 |
:-( |
RouteFun = fun(_, _, Packet) -> put(encode_res, Packet) end, |
501 |
:-( |
ConditionFun = fun({_, _, {WhoU, WhoS}}) -> WhoU =:= <<>> orelse WhoS =:= <<>> end, |
502 |
:-( |
case lists:any(ConditionFun, Items) of |
503 |
|
true -> |
504 |
:-( |
{stop, mongoose_acc:set(hook, result, |
505 |
|
{error, mongoose_xmpp_errors:bad_request()}, Acc)}; |
506 |
|
false -> |
507 |
:-( |
ok = mod_muc_light_db_backend:set_blocking(HostType, jid:to_lus(From), MUCHost, Items), |
508 |
:-( |
mod_muc_light_codec_backend:encode(Blocking, From, To, RouteFun, Acc), |
509 |
:-( |
#xmlel{ children = ResponseChildren } = erase(encode_res), |
510 |
:-( |
{stop, mongoose_acc:set(hook, result, {result, ResponseChildren}, Acc)} |
511 |
|
end; |
512 |
|
{{ok, {set, #blocking{}}}, false} -> |
513 |
:-( |
{stop, mongoose_acc:set(hook, result, |
514 |
|
{error, mongoose_xmpp_errors:bad_request()}, Acc)}; |
515 |
|
_ -> |
516 |
:-( |
{ok, mongoose_acc:set(hook, result, {error, mongoose_xmpp_errors:bad_request()}, Acc)} |
517 |
|
end. |
518 |
|
|
519 |
|
-spec is_muc_room_owner(Acc, Params, Extra) -> {ok, Acc} when |
520 |
|
Acc :: boolean(), |
521 |
|
Params :: map(), |
522 |
|
Extra :: gen_hook:extra(). |
523 |
|
is_muc_room_owner(true, _Params, _Extra) -> |
524 |
:-( |
{ok, true}; |
525 |
|
is_muc_room_owner(_, #{acc := Acc, room := Room, user := User}, _Extra) -> |
526 |
:-( |
{ok, owner == get_affiliation(Acc, Room, User)}. |
527 |
|
|
528 |
|
-spec can_access_room(Acc, Params, Extra) -> {ok, Acc} when |
529 |
|
Acc :: boolean(), |
530 |
|
Params :: map(), |
531 |
|
Extra :: gen_hook:extra(). |
532 |
|
can_access_room(true, _Params, _Extra) -> |
533 |
:-( |
{ok, true}; |
534 |
|
can_access_room(_, #{acc := Acc, room := Room, user := User}, _Extra) -> |
535 |
:-( |
{ok, none =/= get_affiliation(Acc, Room, User)}. |
536 |
|
|
537 |
|
-spec acc_room_affiliations(Acc, Params, Extra) -> {ok, Acc} when |
538 |
|
Acc :: mongoose_acc:t(), |
539 |
|
Params :: map(), |
540 |
|
Extra :: gen_hook:extra(). |
541 |
|
acc_room_affiliations(Acc1, #{room := RoomJid}, _Extra) -> |
542 |
202 |
case get_room_affs_from_acc(Acc1, RoomJid) of |
543 |
|
{error, _} -> |
544 |
172 |
HostType = mongoose_acc:host_type(Acc1), |
545 |
172 |
case mod_muc_light_db_backend:get_aff_users(HostType, jid:to_lus(RoomJid)) of |
546 |
|
{error, not_exists} -> |
547 |
4 |
{ok, Acc1}; |
548 |
|
{ok, _Affs, _Version} = Res -> |
549 |
168 |
{ok, set_room_affs_from_acc(Acc1, RoomJid, Res)} |
550 |
|
end; |
551 |
|
_Affs -> |
552 |
30 |
{ok, Acc1} |
553 |
|
end. |
554 |
|
|
555 |
|
-spec room_exists(Acc, Params, Extra) -> {ok, Acc} when |
556 |
|
Acc :: boolean(), |
557 |
|
Params :: map(), |
558 |
|
Extra :: gen_hook:extra(). |
559 |
|
room_exists(_, #{room := RoomJid}, #{host_type := HostType}) -> |
560 |
153 |
{ok, mod_muc_light_db_backend:room_exists(HostType, jid:to_lus(RoomJid))}. |
561 |
|
|
562 |
|
-spec get_acc_room_affiliations(mongoose_acc:t(), jid:jid()) -> |
563 |
|
{mongoose_acc:t(), versioned_affs() | {error, not_exists}}. |
564 |
|
get_acc_room_affiliations(Acc1, RoomJid) -> |
565 |
202 |
case get_room_affs_from_acc(Acc1, RoomJid) of |
566 |
|
{error, not_exists} -> |
567 |
202 |
Acc2 = mongoose_hooks:acc_room_affiliations(Acc1, RoomJid), |
568 |
202 |
{Acc2, get_room_affs_from_acc(Acc2, RoomJid)}; |
569 |
|
Res -> |
570 |
:-( |
{Acc1, Res} |
571 |
|
end. |
572 |
|
|
573 |
|
-spec get_room_affs_from_acc(mongoose_acc:t(), jid:jid()) -> versioned_affs() | {error, not_exists}. |
574 |
|
get_room_affs_from_acc(Acc, RoomJid) -> |
575 |
722 |
mongoose_acc:get(?MODULE, {affiliations, RoomJid}, {error, not_exists}, Acc). |
576 |
|
|
577 |
|
-spec set_room_affs_from_acc(mongoose_acc:t(), jid:jid(), versioned_affs()) -> mongoose_acc:t(). |
578 |
|
set_room_affs_from_acc(Acc, RoomJid, Affs) -> |
579 |
198 |
mongoose_acc:set(?MODULE, {affiliations, RoomJid}, Affs, Acc). |
580 |
|
|
581 |
|
-spec can_access_identity(Acc, Params, Extra) -> {ok, Acc} when |
582 |
|
Acc :: boolean(), |
583 |
|
Params :: map(), |
584 |
|
Extra :: gen_hook:extra(). |
585 |
|
can_access_identity(true, _Params, _Extra) -> |
586 |
:-( |
{ok, true}; |
587 |
|
can_access_identity(_Acc, _Params, _Extra) -> |
588 |
|
%% User JIDs are explicit in MUC Light but this hook is about appending |
589 |
|
%% 0045 MUC element with user identity and we don't want it |
590 |
:-( |
{ok, false}. |
591 |
|
|
592 |
|
%%==================================================================== |
593 |
|
%% Internal functions |
594 |
|
%%==================================================================== |
595 |
|
|
596 |
|
prepare_config(HostType, RawConfig) -> |
597 |
136 |
Schema = config_schema_for_host_type(HostType), |
598 |
136 |
mod_muc_light_room_config:from_binary_kv(RawConfig, Schema). |
599 |
|
|
600 |
|
prepare_affs(HostType, CreatorJid, RoomUS, #create{aff_users = AffUsers}) -> |
601 |
136 |
CreatorUS = jid:to_lus(CreatorJid), |
602 |
136 |
InitialAffUsers = mod_muc_light_utils:filter_out_prevented(HostType, |
603 |
|
CreatorUS, RoomUS, AffUsers), |
604 |
136 |
Res = process_create_aff_users_if_valid(HostType, CreatorUS, InitialAffUsers), |
605 |
136 |
MaxOccupants = max_occupants(HostType), |
606 |
136 |
case Res of |
607 |
|
{ok, FinalAffUsers} when length(FinalAffUsers) > MaxOccupants -> |
608 |
1 |
{error, max_occupants_reached}; |
609 |
|
_ -> |
610 |
135 |
Res |
611 |
|
end. |
612 |
|
|
613 |
|
max_occupants(HostType) -> |
614 |
136 |
gen_mod:get_module_opt(HostType, ?MODULE, max_occupants). |
615 |
|
|
616 |
|
get_affiliation(Acc, Room, User) -> |
617 |
:-( |
case get_acc_room_affiliations(Acc, Room) of |
618 |
|
{_, {ok, AffUsers, _}} -> |
619 |
:-( |
case lists:keyfind(jid:to_lus(User), 1, AffUsers) of |
620 |
:-( |
{_, Aff} -> Aff; |
621 |
:-( |
_ -> none |
622 |
|
end; |
623 |
|
_ -> |
624 |
:-( |
none |
625 |
|
end. |
626 |
|
|
627 |
|
-spec create_room(mongoose_acc:t(), |
628 |
|
jid:jid(), |
629 |
|
jid:jid(), |
630 |
|
create_req_props(), |
631 |
|
exml:element()) -> |
632 |
|
mongoose_acc:t(). |
633 |
|
create_room(Acc, From, To, Create0, OrigPacket) -> |
634 |
38 |
case try_to_create_room(From, To, Create0) of |
635 |
|
{ok, FinalRoomJid, Details} -> |
636 |
35 |
mod_muc_light_codec_backend:encode({set, Details, To#jid.luser == <<>>}, From, |
637 |
|
FinalRoomJid, make_handler_fun(Acc), Acc); |
638 |
|
{error, exists} -> |
639 |
2 |
mod_muc_light_codec_backend:encode_error({error, {conflict, <<"Room already exists">>}}, |
640 |
|
From, To, OrigPacket, |
641 |
|
Acc); |
642 |
|
{error, bad_request} -> |
643 |
:-( |
mod_muc_light_codec_backend:encode_error({error, bad_request}, From, To, OrigPacket, |
644 |
|
Acc); |
645 |
|
{error, {_, _} = Error} -> |
646 |
:-( |
ErrorText = io_lib:format("~s:~p", tuple_to_list(Error)), |
647 |
:-( |
mod_muc_light_codec_backend:encode_error( |
648 |
|
{error, bad_request, ErrorText}, From, To, OrigPacket, Acc); |
649 |
|
{error, Error} -> |
650 |
1 |
ErrorText = io_lib:format("~p", [Error]), |
651 |
1 |
mod_muc_light_codec_backend:encode_error( |
652 |
|
{error, bad_request, ErrorText}, From, To, OrigPacket, Acc) |
653 |
|
end. |
654 |
|
|
655 |
|
-spec process_create_aff_users_if_valid(HostType :: host_type(), |
656 |
|
Creator :: jid:simple_bare_jid(), |
657 |
|
AffUsers :: aff_users()) -> |
658 |
|
{ok, aff_users()} | {error, bad_request}. |
659 |
|
process_create_aff_users_if_valid(HostType, Creator, AffUsers) -> |
660 |
136 |
case lists:any(fun ({User, _}) when User =:= Creator -> true; |
661 |
34 |
({_, Aff}) -> Aff =:= none end, AffUsers) of |
662 |
|
false -> |
663 |
136 |
process_create_aff_users(Creator, AffUsers, equal_occupants(HostType)); |
664 |
|
true -> |
665 |
:-( |
{error, bad_request} |
666 |
|
end. |
667 |
|
|
668 |
|
equal_occupants(HostType) -> |
669 |
136 |
gen_mod:get_module_opt(HostType, ?MODULE, equal_occupants). |
670 |
|
|
671 |
|
-spec process_create_aff_users(Creator :: jid:simple_bare_jid(), AffUsers :: aff_users(), |
672 |
|
EqualOccupants :: boolean()) -> |
673 |
|
{ok, aff_users()} | {error, bad_request}. |
674 |
|
process_create_aff_users(Creator, AffUsers, EqualOccupants) -> |
675 |
136 |
case mod_muc_light_utils:change_aff_users([{Creator, creator_aff(EqualOccupants)}], AffUsers) of |
676 |
136 |
{ok, FinalAffUsers, _ChangedAffUsers, _JoiningUsers, _LeavingUsers} -> {ok, FinalAffUsers}; |
677 |
:-( |
Error -> Error |
678 |
|
end. |
679 |
|
|
680 |
|
-spec creator_aff(EqualOccupants :: boolean()) -> owner | member. |
681 |
2 |
creator_aff(true) -> member; |
682 |
134 |
creator_aff(false) -> owner. |
683 |
|
|
684 |
|
-spec handle_disco_info_get(From ::jid:jid(), |
685 |
|
To :: jid:jid(), |
686 |
|
DiscoInfo :: disco_info_req_props(), |
687 |
|
Acc :: mongoose_acc:t()) -> |
688 |
|
mongoose_acc:t(). |
689 |
|
handle_disco_info_get(From, To, DiscoInfo, Acc) -> |
690 |
4 |
mod_muc_light_codec_backend:encode({get, DiscoInfo}, From, To, |
691 |
|
make_handler_fun(Acc), Acc). |
692 |
|
|
693 |
|
-spec handle_disco_items_get(HostType :: host_type(), |
694 |
|
Acc :: mongoose_acc:t(), |
695 |
|
From ::jid:jid(), To ::jid:jid(), |
696 |
|
DiscoItems :: disco_items_req_props(), |
697 |
|
OrigPacket :: exml:element()) -> |
698 |
|
mongoose_acc:t(). |
699 |
|
handle_disco_items_get(HostType, Acc, From, To, DiscoItems0, OrigPacket) -> |
700 |
21 |
case catch mod_muc_light_db_backend:get_user_rooms(HostType, jid:to_lus(From), To#jid.lserver) of |
701 |
|
{error, Error} -> |
702 |
:-( |
?LOG_ERROR(#{what => muc_get_user_rooms_failed, |
703 |
|
text => <<"Couldn't get room list for user">>, |
704 |
:-( |
from_jid => From, reason => Error}), |
705 |
:-( |
mod_muc_light_codec_backend:encode_error( |
706 |
|
{error, internal_server_error}, From, To, OrigPacket, Acc); |
707 |
|
Rooms -> |
708 |
21 |
RoomsInfo = get_rooms_info(HostType, lists:sort(Rooms)), |
709 |
21 |
RouteFun = make_handler_fun(Acc), |
710 |
21 |
RoomsPerPage = gen_mod:get_module_opt(HostType, ?MODULE, rooms_per_page), |
711 |
21 |
case apply_rsm(RoomsInfo, length(RoomsInfo), |
712 |
|
page_service_limit(DiscoItems0#disco_items.rsm, RoomsPerPage)) of |
713 |
|
{ok, RoomsInfoSlice, RSMOut} -> |
714 |
19 |
DiscoItems = DiscoItems0#disco_items{ rooms = RoomsInfoSlice, rsm = RSMOut }, |
715 |
19 |
mod_muc_light_codec_backend:encode({get, DiscoItems}, |
716 |
|
From, To, |
717 |
|
RouteFun, Acc); |
718 |
|
{error, item_not_found} -> |
719 |
2 |
mod_muc_light_codec_backend:encode_error({error, item_not_found}, |
720 |
|
From, To, OrigPacket, Acc) |
721 |
|
end |
722 |
|
end. |
723 |
|
|
724 |
|
-spec get_rooms_info(HostType :: mongooseim:host_type(), |
725 |
|
Rooms :: [jid:simple_bare_jid()]) -> [disco_room_info()]. |
726 |
|
get_rooms_info(_HostType, []) -> |
727 |
37 |
[]; |
728 |
|
get_rooms_info(HostType, [{RoomU, _} = RoomUS | RRooms]) -> |
729 |
27 |
{ok, Config, Version} = mod_muc_light_db_backend:get_config(HostType, RoomUS), |
730 |
27 |
RoomName = case lists:keyfind(roomname, 1, Config) of |
731 |
2 |
false -> RoomU; |
732 |
25 |
{_, RoomName0} -> RoomName0 |
733 |
|
end, |
734 |
27 |
[{RoomUS, RoomName, Version} | get_rooms_info(HostType, RRooms)]. |
735 |
|
|
736 |
|
-spec apply_rsm(RoomsInfo :: [disco_room_info()], RoomsInfoLen :: non_neg_integer(), |
737 |
|
RSMIn :: jlib:rsm_in()) -> |
738 |
|
{ok, RoomsInfoSlice :: [disco_room_info()], RSMOut :: jlib:rsm_out()} | {error, item_not_found}. |
739 |
|
apply_rsm(RoomsInfo, _RoomsInfoLen, none) -> |
740 |
2 |
{ok, RoomsInfo, none}; |
741 |
|
apply_rsm(_RoomsInfo, _RoomsInfoLen, #rsm_in{ max = Max }) when Max < 0 -> |
742 |
:-( |
{error, item_not_found}; |
743 |
|
apply_rsm(_RoomsInfo, RoomsInfoLen, #rsm_in{ max = 0 }) -> |
744 |
:-( |
{ok, [], #rsm_out{ count = RoomsInfoLen }}; |
745 |
|
apply_rsm([], 0, #rsm_in{ direction = undefined, id = undefined, index = undefined } = _RSMIn) -> |
746 |
4 |
{ok, [], none}; |
747 |
|
apply_rsm(RoomsInfo, RoomsInfoLen, #rsm_in{ direction = undefined, id = undefined, |
748 |
|
index = undefined } = RSMIn) -> |
749 |
11 |
apply_rsm(RoomsInfo, RoomsInfoLen, RSMIn#rsm_in{ index = 0 }); |
750 |
|
apply_rsm(RoomsInfo, RoomsInfoLen, #rsm_in{ direction = before, id = <<>>, max = Max }) -> |
751 |
2 |
apply_rsm(RoomsInfo, RoomsInfoLen, #rsm_in{ max = Max, index = RoomsInfoLen - Max }); |
752 |
|
apply_rsm(RoomsInfo, RoomsInfoLen, #rsm_in{ index = undefined, direction = Direction, |
753 |
|
id = RoomUSBin, max = Max }) -> |
754 |
2 |
case find_room_pos(RoomUSBin, RoomsInfo) of |
755 |
|
{error, item_not_found} -> |
756 |
2 |
{error, item_not_found}; |
757 |
|
RoomPos -> |
758 |
:-( |
FirstPos = case {Direction, RoomPos - Max} of |
759 |
:-( |
{aft, _} -> RoomPos + 1; |
760 |
:-( |
{before, TooLow} when TooLow < 1 -> 1; |
761 |
:-( |
{before, FirstPos0} -> FirstPos0 |
762 |
|
end, |
763 |
:-( |
[{FirstRoomUS, _, _} | _] = RoomsInfoSlice = lists:sublist(RoomsInfo, FirstPos, Max), |
764 |
:-( |
{LastRoomUS, _, _} = lists:last(RoomsInfoSlice), |
765 |
:-( |
{ok, RoomsInfoSlice, #rsm_out{ count = RoomsInfoLen, |
766 |
|
index = FirstPos - 1, |
767 |
|
first = jid:to_binary(FirstRoomUS), |
768 |
|
last = jid:to_binary(LastRoomUS) }} |
769 |
|
end; |
770 |
|
apply_rsm(RoomsInfo, RoomsInfoLen, #rsm_in{ max = Max, index = Index}) when Index < RoomsInfoLen -> |
771 |
13 |
[{FirstRoomUS, _, _} | _] = RoomsInfoSlice = lists:sublist(RoomsInfo, Index + 1, Max), |
772 |
13 |
{LastRoomUS, _, _} = lists:last(RoomsInfoSlice), |
773 |
13 |
{ok, RoomsInfoSlice, #rsm_out{ count = RoomsInfoLen, |
774 |
|
index = Index, |
775 |
|
first = jid:to_binary(FirstRoomUS), |
776 |
|
last = jid:to_binary(LastRoomUS) }}; |
777 |
|
apply_rsm(_RoomsInfo, _RoomsInfoLen, _RSMIn) -> |
778 |
:-( |
{error, item_not_found}. |
779 |
|
|
780 |
|
-spec page_service_limit(RSMIn :: jlib:rsm_in() | undefined, ServiceMax :: integer()) -> |
781 |
|
jlib:rsm_in() | none. |
782 |
2 |
page_service_limit(none, infinity) -> none; |
783 |
15 |
page_service_limit(none, ServiceMax) -> #rsm_in{ max = ServiceMax }; |
784 |
:-( |
page_service_limit(#rsm_in{ max = Max } = RSMIn, ServiceMax) when Max =< ServiceMax -> RSMIn; |
785 |
4 |
page_service_limit(RSMIn, ServiceMax) -> RSMIn#rsm_in{ max = ServiceMax }. |
786 |
|
|
787 |
|
-spec find_room_pos(RoomUSBin :: binary(), RoomsInfo :: [disco_room_info()]) -> |
788 |
|
pos_integer() | {error, item_not_found}. |
789 |
|
find_room_pos(RoomUSBin, RoomsInfo) -> |
790 |
2 |
case jid:from_binary(RoomUSBin) of |
791 |
:-( |
error -> {error, item_not_found}; |
792 |
2 |
#jid{ luser = RoomU, lserver = RoomS } -> find_room_pos({RoomU, RoomS}, RoomsInfo, 1) |
793 |
|
end. |
794 |
|
|
795 |
|
-spec find_room_pos(RoomUS :: jid:simple_bare_jid(), RoomsInfo :: [disco_room_info()], |
796 |
|
Pos :: pos_integer()) -> pos_integer() | {error, item_not_found}. |
797 |
:-( |
find_room_pos(RoomUS, [{RoomUS, _, _} | _], Pos) -> Pos; |
798 |
4 |
find_room_pos(RoomUS, [_ | RRooms], Pos) -> find_room_pos(RoomUS, RRooms, Pos + 1); |
799 |
2 |
find_room_pos(_, [], _) -> {error, item_not_found}. |
800 |
|
|
801 |
|
-spec handle_blocking(Acc :: mongoose_acc:t(), From :: jid:jid(), To :: jid:jid(), |
802 |
|
BlockingReq :: {get | set, blocking_req_props()}) -> |
803 |
|
{error, bad_request} | ok. |
804 |
|
handle_blocking(Acc, From, To, {get, #blocking{} = Blocking}) -> |
805 |
6 |
HostType = mongoose_acc:host_type(Acc), |
806 |
6 |
BlockingItems = mod_muc_light_db_backend:get_blocking(HostType, jid:to_lus(From), To#jid.lserver), |
807 |
6 |
mod_muc_light_codec_backend:encode({get, Blocking#blocking{ items = BlockingItems }}, |
808 |
|
From, To, make_handler_fun(Acc), Acc); |
809 |
|
handle_blocking(Acc, From, To, {set, #blocking{ items = Items }} = BlockingReq) -> |
810 |
18 |
case lists:any(fun({_, _, {WhoU, WhoS}}) -> WhoU =:= <<>> orelse WhoS =:= <<>> end, Items) of |
811 |
|
true -> |
812 |
:-( |
{error, bad_request}; |
813 |
|
false -> |
814 |
18 |
HostType = mongoose_acc:host_type(Acc), |
815 |
18 |
ok = mod_muc_light_db_backend:set_blocking(HostType, jid:to_lus(From), To#jid.lserver, Items), |
816 |
18 |
mod_muc_light_codec_backend:encode( |
817 |
|
BlockingReq, From, To, make_handler_fun(Acc), Acc), |
818 |
18 |
ok |
819 |
|
end. |
820 |
|
|
821 |
|
-spec bcast_removed_user(Acc :: mongoose_acc:t(), UserJid :: jid:jid(), |
822 |
|
AffectedRooms :: mod_muc_light_db_backend:remove_user_return(), |
823 |
|
Version :: binary()) -> ok. |
824 |
|
bcast_removed_user(Acc, UserJid, AffectedRooms, Version) -> |
825 |
209 |
bcast_removed_user(Acc, UserJid, AffectedRooms, |
826 |
|
Version, mongoose_bin:gen_from_timestamp()). |
827 |
|
|
828 |
|
-spec bcast_removed_user(Acc :: mongoose_acc:t(), UserJID :: jid:jid(), |
829 |
|
AffectedRooms :: mod_muc_light_db_backend:remove_user_return(), |
830 |
|
Version :: binary(), |
831 |
|
PacketID :: binary()) -> ok. |
832 |
|
bcast_removed_user(_Acc, _UserJID, [], _Version, _ID) -> |
833 |
209 |
ok; |
834 |
|
bcast_removed_user(Acc, UserJID, |
835 |
|
[{{RoomU, RoomS}, {ok, OldAffUsers, NewAffUsers, AffUsersChanged, PrevVersion}} |
836 |
|
| RAffected], Version, ID) -> |
837 |
119 |
Affiliations = #affiliations{ |
838 |
|
id = ID, |
839 |
|
prev_version = PrevVersion, |
840 |
|
version = Version, |
841 |
|
aff_users = AffUsersChanged |
842 |
|
}, |
843 |
119 |
Cmd = {set, Affiliations, OldAffUsers, NewAffUsers}, |
844 |
119 |
RoomJid = jid:make_noprep(RoomU, RoomS, <<>>), |
845 |
119 |
mod_muc_light_codec_backend:encode(Cmd, UserJID, RoomJid, make_handler_fun(Acc), Acc), |
846 |
119 |
bcast_removed_user(Acc, UserJID, RAffected, Version, ID); |
847 |
|
bcast_removed_user(Acc, UserJID, [{{RoomU, RoomS} = _RoomUS, Error} | RAffected], Version, ID) -> |
848 |
:-( |
?LOG_ERROR(#{what => muc_remove_user_failed, |
849 |
|
user_jid => jid:to_binary(UserJID), room => RoomU, sub_host => RoomS, |
850 |
:-( |
reason => Error}), |
851 |
:-( |
bcast_removed_user(Acc, UserJID, RAffected, Version, ID). |
852 |
|
|
853 |
|
-spec maybe_forget_rooms(Acc :: mongoose_acc:t(), |
854 |
|
AffectedRooms :: mod_muc_light_db_backend:remove_user_return(), |
855 |
|
Version :: binary()) -> ok. |
856 |
|
maybe_forget_rooms(_Acc, [], _) -> |
857 |
209 |
ok; |
858 |
|
maybe_forget_rooms(Acc, [{RoomUS, {ok, _, NewAffUsers, _, _}} | RAffectedRooms], Version) -> |
859 |
119 |
mod_muc_light_room:maybe_forget(Acc, RoomUS, NewAffUsers, Version), |
860 |
119 |
maybe_forget_rooms(Acc, RAffectedRooms, Version). |
861 |
|
|
862 |
|
make_handler_fun(Acc) -> |
863 |
203 |
fun(From, To, Packet) -> ejabberd_router:route(From, To, Acc, Packet) end. |
864 |
|
|
865 |
|
-spec config_metrics(mongooseim:host_type()) -> [{gen_mod:opt_key(), gen_mod:opt_value()}]. |
866 |
|
config_metrics(HostType) -> |
867 |
12 |
mongoose_module_metrics:opts_for_module(HostType, ?MODULE, [backend]). |