./ct_report/coverage/mod_muc_light_codec_modern.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : mod_muc_light_codec_modern.erl
3 %%% Author : Piotr Nosek <piotr.nosek@erlang-solutions.com>
4 %%% Purpose : MUC Light codec for modern syntax
5 %%% Created : 29 Sep 2015 by Piotr Nosek <piotr.nosek@erlang-solutions.com>
6 %%%----------------------------------------------------------------------
7
8 -module(mod_muc_light_codec_modern).
9 -author('piotr.nosek@erlang-solutions.com').
10
11 -behaviour(mod_muc_light_codec_backend).
12
13 %% API
14 -export([decode/4, encode/5, encode_error/5]).
15
16 -include("mongoose.hrl").
17 -include("jlib.hrl").
18 -include("mod_muc_light.hrl").
19
20 -define(HANDLE_PARSE_ERROR(Function, From, IQ),
21 {error, Reason} ->
22 ?LOG_WARNING(#{what => muc_parse_failed, parser => Function,
23 iq => IQ, from_jid => jid:to_binary(From),
24 reason => Reason}),
25 {error, bad_request}
26 catch Class:Reason:Stacktrace ->
27 ?LOG_WARNING(#{what => muc_parse_failed, parser => Function,
28 iq => IQ, from_jid => jid:to_binary(From),
29 class => Class, reason => Reason, stacktrace => Stacktrace}),
30 {error, bad_request}).
31
32 -type bad_request() :: bad_request | {bad_request, binary()}.
33
34 %%====================================================================
35 %% API
36 %%====================================================================
37
38 -spec decode(From :: jid:jid(), To :: jid:jid(), Stanza :: exml:element(),
39 Acc :: mongoose_acc:t()) ->
40 mod_muc_light_codec_backend:decode_result().
41 decode(_From, #jid{ lresource = Resource }, _Stanza, _Acc) when Resource =/= <<>> ->
42 1 {error, {bad_request, <<"Resource expected to be empty">>}};
43 decode(_From, _To, #xmlel{ name = <<"message">> } = Stanza, _Acc) ->
44 426 decode_message(Stanza);
45 decode(From, _To, #xmlel{ name = <<"iq">> } = Stanza, _Acc) ->
46 434 decode_iq(From, jlib:iq_query_info(Stanza));
47 decode(_, _, _, _) ->
48
:-(
{error, {bad_request, <<"Failed to decode unknown format">>}}.
49
50 -spec encode(Request :: muc_light_encode_request(), OriginalSender :: jid:jid(),
51 RoomJID :: jid:jid(),
52 HandleFun :: mod_muc_light_codec_backend:encoded_packet_handler(),
53 Acc :: mongoose_acc:t()) -> mongoose_acc:t().
54 encode({#msg{} = Msg, AffUsers}, Sender, RoomBareJid, HandleFun, Acc) ->
55 419 US = jid:to_lus(Sender),
56 419 FromNick = jid:to_binary(US),
57 419 Aff = get_sender_aff(AffUsers, US),
58 419 {RoomJID, RoomBin} = jids_from_room_with_resource(RoomBareJid, FromNick),
59 419 Attrs = [
60 {<<"id">>, Msg#msg.id},
61 {<<"type">>, <<"groupchat">>},
62 {<<"from">>, RoomBin}
63 ],
64 419 MsgForArch = #xmlel{ name = <<"message">>, attrs = Attrs, children = Msg#msg.children },
65 419 TS = mongoose_acc:timestamp(Acc),
66 419 EventData = #{from_nick => FromNick,
67 from_jid => Sender,
68 room_jid => RoomBareJid,
69 affiliation => Aff,
70 role => mod_muc_light_utils:light_aff_to_muc_role(Aff),
71 timestamp => TS},
72 419 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
73 419 Packet1 = #xmlel{ children = Children }
74 = mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData),
75 419 lists:foreach(
76 fun({{U, S}, _}) ->
77 1029 msg_to_aff_user(RoomJID, U, S, Attrs, Children, HandleFun)
78 end, AffUsers),
79 419 mongoose_acc:update_stanza(#{from_jid => RoomJID,
80 to_jid => RoomBareJid,
81 element => Packet1}, Acc);
82 encode(OtherCase, Sender, RoomBareJid, HandleFun, Acc) ->
83 488 {RoomJID, RoomBin} = jids_from_room_with_resource(RoomBareJid, <<>>),
84 488 case encode_iq(OtherCase, Sender, RoomJID, RoomBin, HandleFun, Acc) of
85 {reply, ID} ->
86 284 IQRes = make_iq_result(RoomBin, jid:to_binary(Sender), ID, <<>>, undefined),
87 284 HandleFun(RoomJID, Sender, IQRes);
88 {reply, XMLNS, Els, ID} ->
89 41 IQRes = make_iq_result(RoomBin, jid:to_binary(Sender), ID, XMLNS, Els),
90 41 HandleFun(RoomJID, Sender, IQRes);
91 {reply, FromJID, FromBin, XMLNS, Els, ID} ->
92 163 IQRes = make_iq_result(FromBin, jid:to_binary(Sender), ID, XMLNS, Els),
93 163 HandleFun(FromJID, Sender, IQRes)
94 end.
95
96 get_sender_aff(Users, US) ->
97 419 case lists:keyfind(US, 1, Users) of
98 419 {US, Aff} -> Aff;
99
:-(
_ -> undefined
100 end.
101
102 -spec encode_error(
103 ErrMsg :: tuple(), OrigFrom :: jid:jid(), OrigTo :: jid:jid(),
104 OrigPacket :: exml:element(), Acc :: mongoose_acc:t()) ->
105 mongoose_acc:t().
106 encode_error(ErrMsg, OrigFrom, OrigTo, OrigPacket, Acc) ->
107 16 mod_muc_light_codec_backend:encode_error(ErrMsg, [], OrigFrom, OrigTo, OrigPacket, Acc).
108
109 %%====================================================================
110 %% Message decoding
111 %%====================================================================
112
113 -spec decode_message(Packet :: exml:element()) ->
114 {ok, muc_light_packet()} | {error, bad_request} | ignore.
115 decode_message(#xmlel{ attrs = Attrs, children = Children }) ->
116 426 decode_message_by_type(lists:keyfind(<<"type">>, 1, Attrs),
117 lists:keyfind(<<"id">>, 1, Attrs), Children).
118
119 -spec decode_message_by_type(Type :: {binary(), binary()} | false,
120 Id :: {binary(), binary()} | false,
121 Children :: [jlib:xmlch()]) ->
122 {ok, msg()} | {error, bad_request} | ignore.
123 decode_message_by_type({_, <<"groupchat">>}, Id, Children) ->
124 421 {ok, #msg{ children = Children, id = ensure_id(Id) }};
125 decode_message_by_type({_, <<"error">>}, _, _) ->
126 5 ignore;
127 decode_message_by_type(_, _, _) ->
128
:-(
{error, bad_request}.
129
130 -spec ensure_id(Id :: {binary(), binary()} | false) -> binary().
131 279 ensure_id(false) -> mongoose_bin:gen_from_timestamp();
132 142 ensure_id({_, Id}) -> Id.
133
134 %%====================================================================
135 %% IQ decoding
136 %%====================================================================
137
138 -spec decode_iq(From :: jid:jid(), IQ :: jlib:iq()) ->
139 {ok, muc_light_packet() | muc_light_disco() | jlib:iq()} | {error, bad_request()} | ignore.
140 decode_iq(_From, #iq{ xmlns = ?NS_MUC_LIGHT_CONFIGURATION, type = get,
141 sub_el = QueryEl, id = ID }) ->
142 12 Version = exml_query:path(QueryEl, [{element, <<"version">>}, cdata], <<>>),
143 12 {ok, {get, #config{ id = ID, prev_version = Version }}};
144 decode_iq(From, IQ = #iq{ xmlns = ?NS_MUC_LIGHT_CONFIGURATION, type = set,
145 sub_el = #xmlel{ children = QueryEls }, id = ID }) ->
146 14 try parse_config(QueryEls) of
147 {ok, RawConfig} ->
148 14 {ok, {set, #config{ id = ID, raw_config = RawConfig }}};
149
:-(
?HANDLE_PARSE_ERROR(parse_config, From, IQ)
150 end;
151 decode_iq(_From, #iq{ xmlns = ?NS_MUC_LIGHT_AFFILIATIONS, type = get,
152 sub_el = QueryEl, id = ID }) ->
153 6 Version = exml_query:path(QueryEl, [{element, <<"version">>}, cdata], <<>>),
154 6 {ok, {get, #affiliations{ id = ID, prev_version = Version }}};
155 decode_iq(From, IQ = #iq{ xmlns = ?NS_MUC_LIGHT_AFFILIATIONS, type = set,
156 sub_el = #xmlel{ children = QueryEls }, id = ID }) ->
157 148 try parse_aff_users(QueryEls) of
158 {ok, AffUsers} ->
159 148 {ok, {set, #affiliations{ id = ID, aff_users = AffUsers }}};
160
:-(
?HANDLE_PARSE_ERROR(parse_aff_users, From, IQ)
161 end;
162 decode_iq(_From, #iq{ xmlns = ?NS_MUC_LIGHT_INFO, type = get, sub_el = QueryEl, id = ID }) ->
163 3 Version = exml_query:path(QueryEl, [{element, <<"version">>}, cdata], <<>>),
164 3 {ok, {get, #info{
165 id = ID,
166 prev_version = Version
167 }}};
168 decode_iq(_From, #iq{ xmlns = ?NS_MUC_LIGHT_BLOCKING, type = get, id = ID }) ->
169 4 {ok, {get, #blocking{ id = ID }}};
170 decode_iq(From, IQ = #iq{ xmlns = ?NS_MUC_LIGHT_BLOCKING, type = set,
171 sub_el = #xmlel{ children = QueryEls }, id = ID }) ->
172 18 try parse_blocking_list(QueryEls) of
173 {ok, BlockingList} ->
174 18 {ok, {set, #blocking{ id = ID, items = BlockingList }}};
175
:-(
?HANDLE_PARSE_ERROR(parse_blocking_list, From, IQ)
176 end;
177 decode_iq(From, IQ = #iq{ xmlns = ?NS_MUC_LIGHT_CREATE, type = set, sub_el = QueryEl, id = ID }) ->
178 166 ConfigEl = exml_query:path(QueryEl, [{element, <<"configuration">>}]),
179 166 OccupantsEl = exml_query:path(QueryEl, [{element, <<"occupants">>}]),
180 166 try parse_config(safe_get_children(ConfigEl)) of
181 {ok, RawConfig} ->
182 166 try parse_aff_users(safe_get_children(OccupantsEl)) of
183 {ok, AffUsers} ->
184 166 {ok, {set, #create{ id = ID, raw_config = RawConfig, aff_users = AffUsers }}};
185
:-(
?HANDLE_PARSE_ERROR(parse_aff_users, From, IQ)
186 end;
187
:-(
?HANDLE_PARSE_ERROR(parse_config, From, IQ)
188 end;
189 decode_iq(_From, #iq{ xmlns = ?NS_MUC_LIGHT_DESTROY, type = set, id = ID }) ->
190 6 {ok, {set, #destroy{ id = ID }}};
191 decode_iq(_From, #iq{ xmlns = ?NS_DISCO_ITEMS, type = get, id = ID} = IQ) ->
192 17 {ok, {get, #disco_items{ id = ID, rsm = jlib:rsm_decode(IQ) }}};
193 decode_iq(_From, #iq{ xmlns = ?NS_DISCO_INFO, type = get, id = ID}) ->
194 4 {ok, {get, #disco_info{ id = ID }}};
195 decode_iq(_From, #iq{ type = error }) ->
196
:-(
ignore;
197 decode_iq(_From, #iq{} = IQ) ->
198 36 {ok, IQ};
199 decode_iq(_, _) ->
200
:-(
{error, {bad_request, <<"Unknown IQ format">>}}.
201
202 %% ------------------ Parsers ------------------
203
204 -spec parse_config(Els :: [jlib:xmlch()]) -> {ok, mod_muc_light_room_config:binary_kv()}
205 | {error, bad_request()}.
206 parse_config(Els) ->
207 180 parse_config(Els, []).
208
209 -spec parse_config(Els :: [jlib:xmlch()], ConfigAcc :: mod_muc_light_room_config:binary_kv()) ->
210 {ok, mod_muc_light_room_config:binary_kv()} | {error, bad_request()}.
211 parse_config([], ConfigAcc) ->
212 180 {ok, ConfigAcc};
213 parse_config([#xmlel{ name = <<"version">> } | _], _) ->
214
:-(
{error, {bad_request, <<"Version element not allowed">>}};
215 parse_config([#xmlel{ name = Key, children = [ #xmlcdata{ content = Value } ] } | REls],
216 ConfigAcc) ->
217 67 parse_config(REls, [{Key, Value} | ConfigAcc]);
218 parse_config([_ | REls], ConfigAcc) ->
219
:-(
parse_config(REls, ConfigAcc).
220
221 -spec parse_aff_users(Els :: [jlib:xmlch()]) ->
222 {ok, aff_users()} | {error, bad_request()}.
223 parse_aff_users(Els) ->
224 314 parse_aff_users(Els, []).
225
226 -spec parse_aff_users(Els :: [jlib:xmlch()], AffUsersAcc :: aff_users()) ->
227 {ok, aff_users()} | {error, bad_request()}.
228 parse_aff_users([], AffUsersAcc) ->
229 314 {ok, AffUsersAcc};
230 parse_aff_users([#xmlcdata{} | RItemsEls], AffUsersAcc) ->
231
:-(
parse_aff_users(RItemsEls, AffUsersAcc);
232 parse_aff_users([#xmlel{ name = <<"user">>, attrs = [{<<"affiliation">>, AffBin}],
233 children = [ #xmlcdata{ content = JIDBin } ] } | RItemsEls],
234 AffUsersAcc) ->
235 353 #jid{} = JID = jid:from_binary(JIDBin),
236 353 Aff = mod_muc_light_utils:b2aff(AffBin),
237 353 parse_aff_users(RItemsEls, [{jid:to_lus(JID), Aff} | AffUsersAcc]);
238 parse_aff_users(_, _) ->
239
:-(
{error, {bad_request, <<"Failed to parse affiliations">>}}.
240
241 -spec parse_blocking_list(Els :: [jlib:xmlch()]) -> {ok, [blocking_item()]} | {error, bad_request}.
242 parse_blocking_list(ItemsEls) ->
243 18 parse_blocking_list(ItemsEls, []).
244
245 -spec parse_blocking_list(Els :: [jlib:xmlch()], ItemsAcc :: [blocking_item()]) ->
246 {ok, [blocking_item()]} | {error, bad_request}.
247 parse_blocking_list([], ItemsAcc) ->
248 18 {ok, ItemsAcc};
249 parse_blocking_list([#xmlel{ name = WhatBin, attrs = [{<<"action">>, ActionBin}],
250 children = [ #xmlcdata{ content = JIDBin } ] } | RItemsEls],
251 ItemsAcc) ->
252 22 #jid{} = JID = jid:from_binary(JIDBin),
253 22 Action = b2action(ActionBin),
254 22 What = b2what(WhatBin),
255 22 parse_blocking_list(RItemsEls, [{What, Action, jid:to_lus(JID)} | ItemsAcc]);
256 parse_blocking_list(_, _) ->
257
:-(
{error, bad_request}.
258
259 %%====================================================================
260 %% Encoding
261 %%====================================================================
262
263 encode_iq({get, #disco_info{ id = ID }}, Sender, RoomJID, _RoomBin, _HandleFun, Acc) ->
264 4 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
265 4 IdentityXML = mongoose_disco:identities_to_xml([identity()]),
266 4 FeatureXML = mongoose_disco:get_muc_features(HostType, Sender, RoomJID, <<>>, <<>>,
267 [?NS_MUC_LIGHT]),
268 4 DiscoEls = IdentityXML ++ FeatureXML,
269 4 {reply, ?NS_DISCO_INFO, DiscoEls, ID};
270 encode_iq({get, #disco_items{ rooms = Rooms, id = ID, rsm = RSMOut }},
271 _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) ->
272 16 DiscoEls = [ #xmlel{ name = <<"item">>,
273 attrs = [{<<"jid">>, <<RoomU/binary, $@, RoomS/binary>>},
274 {<<"name">>, RoomName},
275 {<<"version">>, RoomVersion}] }
276 16 || {{RoomU, RoomS}, RoomName, RoomVersion} <- Rooms ],
277 16 {reply, ?NS_DISCO_ITEMS, jlib:rsm_encode(RSMOut) ++ DiscoEls, ID};
278 encode_iq({get, #config{ prev_version = SameVersion, version = SameVersion, id = ID }},
279 _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) ->
280 1 {reply, ID};
281 encode_iq({get, #config{} = Config},
282 _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) ->
283 11 ConfigEls = [ kv_to_el(Field) || Field <- [{<<"version">>, Config#config.version}
284 | Config#config.raw_config] ],
285 11 {reply, ?NS_MUC_LIGHT_CONFIGURATION, ConfigEls, Config#config.id};
286 encode_iq({get, #affiliations{ prev_version = SameVersion, version = SameVersion, id = ID }},
287 _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) ->
288 2 {reply, ID};
289 encode_iq({get, #affiliations{ version = Version } = Affs},
290 _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) ->
291 4 AffEls = [ aff_user_to_el(AffUser) || AffUser <- Affs#affiliations.aff_users ],
292 4 {reply, ?NS_MUC_LIGHT_AFFILIATIONS, [kv_to_el(<<"version">>, Version) | AffEls],
293 Affs#affiliations.id};
294 encode_iq({get, #info{ prev_version = SameVersion, version = SameVersion, id = ID }},
295 _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) ->
296
:-(
{reply, ID};
297 encode_iq({get, #info{ version = Version } = Info},
298 _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) ->
299 3 ConfigEls = [ kv_to_el(Field) || Field <- Info#info.raw_config ],
300 3 AffEls = [ aff_user_to_el(AffUser) || AffUser <- Info#info.aff_users ],
301 3 InfoEls = [
302 kv_to_el(<<"version">>, Version),
303 #xmlel{ name = <<"configuration">>, children = ConfigEls },
304 #xmlel{ name = <<"occupants">>, children = AffEls }
305 ],
306 3 {reply, ?NS_MUC_LIGHT_INFO, InfoEls, Info#info.id};
307 encode_iq({set, #affiliations{} = Affs, OldAffUsers, NewAffUsers},
308 _Sender, RoomJID, RoomBin, HandleFun, Acc) ->
309 246 Attrs = [
310 {<<"id">>, Affs#affiliations.id},
311 {<<"type">>, <<"groupchat">>},
312 {<<"from">>, RoomBin}
313 ],
314
315 246 AllAffsEls = [ aff_user_to_el(AffUser) || AffUser <- Affs#affiliations.aff_users ],
316 246 VersionEl = kv_to_el(<<"version">>, Affs#affiliations.version),
317 246 NotifForCurrentNoPrevVersion = [ VersionEl | AllAffsEls ],
318 246 MsgForArch = #xmlel{ name = <<"message">>, attrs = Attrs,
319 children = msg_envelope(?NS_MUC_LIGHT_AFFILIATIONS,
320 NotifForCurrentNoPrevVersion) },
321 246 EventData = room_event(Acc, RoomJID),
322 246 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
323 246 #xmlel{children = FinalChildrenForCurrentNoPrevVersion}
324 = mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData),
325 246 FinalChildrenForCurrent = inject_prev_version(FinalChildrenForCurrentNoPrevVersion,
326 Affs#affiliations.prev_version),
327 246 bcast_aff_messages(RoomJID, OldAffUsers, NewAffUsers, Attrs, VersionEl,
328 FinalChildrenForCurrent, HandleFun),
329
330 246 {reply, Affs#affiliations.id};
331 encode_iq({get, #blocking{} = Blocking},
332 _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) ->
333 3 BlockingEls = [ blocking_to_el(BlockingItem) || BlockingItem <- Blocking#blocking.items ],
334 3 {reply, ?NS_MUC_LIGHT_BLOCKING, BlockingEls, Blocking#blocking.id};
335 encode_iq({set, #blocking{ id = ID }},
336 _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) ->
337 17 {reply, ID};
338 encode_iq({set, #create{} = Create, UniqueRequested},
339 _Sender, RoomJID, RoomBin, HandleFun, Acc) ->
340 163 Attrs = [
341 {<<"id">>, Create#create.id},
342 {<<"type">>, <<"groupchat">>},
343 {<<"from">>, RoomBin}
344 ],
345
346 163 VersionEl = kv_to_el(<<"version">>, Create#create.version),
347 163 bcast_aff_messages(RoomJID, [], Create#create.aff_users, Attrs, VersionEl, [], HandleFun),
348
349 163 AllAffsEls = [ aff_user_to_el(AffUser) || AffUser <- Create#create.aff_users ],
350 163 MsgForArch = #xmlel{ name = <<"message">>, attrs = Attrs,
351 children = msg_envelope(?NS_MUC_LIGHT_AFFILIATIONS, AllAffsEls) },
352 163 EventData = room_event(Acc, RoomJID),
353 163 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
354 163 mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData),
355
356 %% IQ reply "from"
357 %% Sent from service JID when unique room was requested
358 163 {ResFromJID, ResFromBin} = case UniqueRequested of
359 7 true -> {#jid{lserver = RoomJID#jid.lserver},
360 RoomJID#jid.lserver};
361 156 false -> {RoomJID, RoomBin}
362 end,
363 163 {reply, ResFromJID, ResFromBin, <<>>, undefined, Create#create.id};
364 encode_iq({set, #destroy{ id = ID }, AffUsers},
365 _Sender, RoomJID, RoomBin, HandleFun, _Acc) ->
366 6 Attrs = [
367 {<<"id">>, ID},
368 {<<"type">>, <<"groupchat">>},
369 {<<"from">>, RoomBin}
370 ],
371
372 6 lists:foreach(
373 fun({{U, S}, _}) ->
374 15 NoneAffEnveloped = msg_envelope(?NS_MUC_LIGHT_AFFILIATIONS,
375 [aff_user_to_el({{U, S}, none})]),
376 15 DestroyEnveloped = [ #xmlel{ name = <<"x">>,
377 attrs = [{<<"xmlns">>, ?NS_MUC_LIGHT_DESTROY}] }
378 | NoneAffEnveloped ],
379 15 msg_to_aff_user(RoomJID, U, S, Attrs, DestroyEnveloped, HandleFun)
380 end, AffUsers),
381
382 6 {reply, ID};
383 encode_iq({set, #config{} = Config, AffUsers},
384 _Sender, RoomJID, RoomBin, HandleFun, Acc) ->
385 12 MsgForArch = encode_set_config(Config, RoomBin),
386 12 EventData = room_event(Acc, RoomJID),
387 12 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
388 12 #xmlel{ children = FinalConfigNotif }
389 = mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData),
390
391 12 lists:foreach(
392 fun({{U, S}, _}) ->
393 36 msg_to_aff_user(RoomJID, U, S, MsgForArch#xmlel.attrs, FinalConfigNotif, HandleFun)
394 end, AffUsers),
395
396 12 {reply, Config#config.id}.
397
398 encode_set_config(Config, RoomBin) ->
399 12 Attrs = [
400 {<<"id">>, Config#config.id},
401 {<<"type">>, <<"groupchat">>},
402 {<<"from">>, RoomBin}
403 ],
404 12 ConfigEls = [ kv_to_el(ConfigField) || ConfigField <- Config#config.raw_config ],
405 12 ConfigNotif = [ kv_to_el(<<"prev-version">>, Config#config.prev_version),
406 kv_to_el(<<"version">>, Config#config.version)
407 | ConfigEls ],
408 12 #xmlel{name = <<"message">>, attrs = Attrs,
409 children = msg_envelope(?NS_MUC_LIGHT_CONFIGURATION, ConfigNotif) }.
410
411 %% --------------------------- Helpers ---------------------------
412
413 -spec identity() -> mongoose_disco:identity().
414 identity() ->
415 4 #{category => <<"conference">>, type => <<"text">>, name => <<"MUC Light (modern)">>}.
416
417 -spec aff_user_to_el(aff_user()) -> exml:element().
418 aff_user_to_el({User, Aff}) ->
419 1282 #xmlel{ name = <<"user">>,
420 attrs = [{<<"affiliation">>, mod_muc_light_utils:aff2b(Aff)}],
421 children = [#xmlcdata{ content = jid:to_binary(User) }] }.
422
423 -spec blocking_to_el(blocking_item()) -> exml:element().
424 blocking_to_el({What, Action, Who}) ->
425 2 #xmlel{ name = what2b(What),
426 attrs = [{<<"action">>, action2b(Action)}],
427 children = [#xmlcdata{ content = jid:to_binary(Who) }] }.
428
429 -spec kv_to_el({binary(), binary()}) -> exml:element().
430 kv_to_el({Key, Value}) ->
431 55 kv_to_el(Key, Value).
432
433 -spec kv_to_el(binary(), binary()) -> exml:element().
434 kv_to_el(Key, Value) ->
435 741 #xmlel{ name = Key, children = [#xmlcdata{ content = Value }] }.
436
437 -spec msg_envelope(XMLNS :: binary(), Children :: [jlib:xmlch()]) -> [exml:element()].
438 msg_envelope(XMLNS, Children) ->
439 1041 [ #xmlel{ name = <<"x">>, attrs = [{<<"xmlns">>, XMLNS}], children = Children },
440 #xmlel{ name = <<"body">> } ].
441
442 -spec inject_prev_version(IQChildren :: [jlib:xmlch()], PrevVersion :: binary()) -> [jlib:xmlch()].
443 inject_prev_version([#xmlel{ name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_MUC_LIGHT_AFFILIATIONS}],
444 children = Items} = XEl | REls], PrevVersion) ->
445 246 [XEl#xmlel{ children = [kv_to_el(<<"prev-version">>, PrevVersion) | Items] } | REls];
446 inject_prev_version([El | REls], PrevVersion) ->
447
:-(
[El | inject_prev_version(REls, PrevVersion)].
448
449 -spec bcast_aff_messages(From :: jid:jid(), OldAffUsers :: aff_users(),
450 NewAffUsers :: aff_users(), Attrs :: [{binary(), binary()}],
451 VersionEl :: exml:element(), Children :: [jlib:xmlch()],
452 HandleFun :: mod_muc_light_codec_backend:encoded_packet_handler()) -> ok.
453 bcast_aff_messages(_, [], [], _, _, _, _) ->
454 409 ok;
455 bcast_aff_messages(From, [{User, _} | ROldAffUsers], [], Attrs, VersionEl, Children, HandleFun) ->
456 112 msg_to_leaving_user(From, User, Attrs, HandleFun),
457 112 bcast_aff_messages(From, ROldAffUsers, [], Attrs, VersionEl, Children, HandleFun);
458 bcast_aff_messages(From, [{{ToU, ToS} = User, _} | ROldAffUsers], [{User, _} | RNewAffUsers],
459 Attrs, VersionEl, Children, HandleFun) ->
460 226 msg_to_aff_user(From, ToU, ToS, Attrs, Children, HandleFun),
461 226 bcast_aff_messages(From, ROldAffUsers, RNewAffUsers, Attrs, VersionEl, Children, HandleFun);
462 bcast_aff_messages(From, [{User1, _} | ROldAffUsers], [{User2, _} | _] = NewAffUsers,
463 Attrs, VersionEl, Children, HandleFun) when User1 < User2 ->
464 72 msg_to_leaving_user(From, User1, Attrs, HandleFun),
465 72 bcast_aff_messages(From, ROldAffUsers, NewAffUsers, Attrs, VersionEl, Children, HandleFun);
466 bcast_aff_messages(From, OldAffUsers, [{{ToU, ToS}, _} = AffUser | RNewAffUsers],
467 Attrs, VersionEl, Children, HandleFun) ->
468 421 NotifForNewcomer = msg_envelope(?NS_MUC_LIGHT_AFFILIATIONS,
469 [ VersionEl, aff_user_to_el(AffUser) ]),
470 421 msg_to_aff_user(From, ToU, ToS, Attrs, NotifForNewcomer, HandleFun),
471 421 bcast_aff_messages(From, OldAffUsers, RNewAffUsers, Attrs, VersionEl, Children, HandleFun).
472
473 -spec msg_to_leaving_user(From :: jid:jid(), User :: jid:simple_bare_jid(),
474 Attrs :: [{binary(), binary()}],
475 HandleFun :: mod_muc_light_codec_backend:encoded_packet_handler()) -> ok.
476 msg_to_leaving_user(From, {ToU, ToS} = User, Attrs, HandleFun) ->
477 184 NotifForLeaving = msg_envelope(?NS_MUC_LIGHT_AFFILIATIONS, [ aff_user_to_el({User, none}) ]),
478 184 msg_to_aff_user(From, ToU, ToS, Attrs, NotifForLeaving, HandleFun).
479
480 -spec msg_to_aff_user(From :: jid:jid(), ToU :: jid:luser(), ToS :: jid:lserver(),
481 Attrs :: [{binary(), binary()}], Children :: [jlib:xmlch()],
482 HandleFun :: mod_muc_light_codec_backend:encoded_packet_handler()) -> ok.
483 msg_to_aff_user(From, ToU, ToS, Attrs, Children, HandleFun) ->
484 1911 To = jid:make_noprep(ToU, ToS, <<>>),
485 1911 ToBin = jid:to_binary({ToU, ToS, <<>>}),
486 1911 Packet = #xmlel{ name = <<"message">>, attrs = [{<<"to">>, ToBin} | Attrs],
487 children = Children },
488 1911 HandleFun(From, To, Packet).
489
490 -spec jids_from_room_with_resource(jid:jid(), binary()) ->
491 {jid:jid(), binary()}.
492 jids_from_room_with_resource(RoomJID, Resource) ->
493 907 From = jid:replace_resource(RoomJID, Resource),
494 907 FromBin = jid:to_binary(jid:to_lower(From)),
495 907 {From, FromBin}.
496
497 -spec make_iq_result(FromBin :: binary(), ToBin :: binary(), ID :: binary(),
498 XMLNS :: binary(), Els :: [jlib:xmlch()] | undefined) -> exml:element().
499 make_iq_result(FromBin, ToBin, ID, XMLNS, Els) ->
500 488 Attrs = [
501 {<<"from">>, FromBin},
502 {<<"to">>, ToBin},
503 {<<"id">>, ID},
504 {<<"type">>, <<"result">>}
505 ],
506 488 Query = make_query_el(XMLNS, Els),
507 488 #xmlel{ name = <<"iq">>, attrs = Attrs, children = Query }.
508
509 -spec make_query_el(binary(), [jlib:xmlch()] | undefined) -> [exml:element()].
510 make_query_el(_, undefined) ->
511 447 [];
512 make_query_el(XMLNS, Els) ->
513 41 [#xmlel{ name = <<"query">>, attrs = [{<<"xmlns">>, XMLNS}], children = Els }].
514
515 %%====================================================================
516 %% Common helpers and internal functions
517 %%====================================================================
518
519 -spec safe_get_children(exml:element() | term()) -> [exml:element() | exml:cdata()].
520 332 safe_get_children(#xmlel{ children = Ch }) -> Ch;
521
:-(
safe_get_children(_) -> [].
522
523 -spec b2action(ActionBin :: binary()) -> atom().
524 8 b2action(<<"allow">>) -> allow;
525 14 b2action(<<"deny">>) -> deny.
526
527 -spec action2b(Action :: atom()) -> binary().
528
:-(
action2b(allow) -> <<"allow">>;
529 2 action2b(deny) -> <<"deny">>.
530
531 -spec b2what(WhatBin :: binary()) -> atom().
532 17 b2what(<<"user">>) -> user;
533 5 b2what(<<"room">>) -> room.
534
535 -spec what2b(What :: atom()) -> binary().
536 1 what2b(user) -> <<"user">>;
537 1 what2b(room) -> <<"room">>.
538
539 -spec room_event(mongoose_acc:t(), jid:jid()) -> mod_muc:room_event_data().
540 room_event(Acc, RoomJID) ->
541 421 TS = mongoose_acc:timestamp(Acc),
542 421 #{from_nick => <<>>,
543 from_jid => RoomJID,
544 room_jid => RoomJID,
545 affiliation => owner,
546 role => moderator,
547 timestamp => TS}.
Line Hits Source