./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 304 decode_message(Stanza);
45 decode(From, _To, #xmlel{ name = <<"iq">> } = Stanza, _Acc) ->
46 299 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 297 US = jid:to_lus(Sender),
56 297 FromNick = jid:to_binary(US),
57 297 Aff = get_sender_aff(AffUsers, US),
58 297 {RoomJID, RoomBin} = jids_from_room_with_resource(RoomBareJid, FromNick),
59 297 Attrs = [
60 {<<"id">>, Msg#msg.id},
61 {<<"type">>, <<"groupchat">>},
62 {<<"from">>, RoomBin}
63 ],
64 297 MsgForArch = #xmlel{ name = <<"message">>, attrs = Attrs, children = Msg#msg.children },
65 297 TS = mongoose_acc:timestamp(Acc),
66 297 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 297 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
73 297 Packet1 = #xmlel{ children = Children }
74 = mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData),
75 297 lists:foreach(
76 fun({{U, S}, _}) ->
77 727 msg_to_aff_user(RoomJID, U, S, Attrs, Children, HandleFun)
78 end, AffUsers),
79 297 mongoose_acc:update_stanza(#{from_jid => RoomJID,
80 to_jid => RoomBareJid,
81 element => Packet1}, Acc);
82 encode(OtherCase, Sender, RoomBareJid, HandleFun, Acc) ->
83 377 {RoomJID, RoomBin} = jids_from_room_with_resource(RoomBareJid, <<>>),
84 377 case encode_iq(OtherCase, Sender, RoomJID, RoomBin, HandleFun, Acc) of
85 {reply, ID} ->
86 226 IQRes = make_iq_result(RoomBin, jid:to_binary(Sender), ID, <<>>, undefined),
87 226 HandleFun(RoomJID, Sender, IQRes);
88 {reply, XMLNS, Els, ID} ->
89 40 IQRes = make_iq_result(RoomBin, jid:to_binary(Sender), ID, XMLNS, Els),
90 40 HandleFun(RoomJID, Sender, IQRes);
91 {reply, FromJID, FromBin, XMLNS, Els, ID} ->
92 111 IQRes = make_iq_result(FromBin, jid:to_binary(Sender), ID, XMLNS, Els),
93 111 HandleFun(FromJID, Sender, IQRes)
94 end.
95
96 get_sender_aff(Users, US) ->
97 297 case lists:keyfind(US, 1, Users) of
98 297 {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 17 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 304 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 299 {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 192 ensure_id(false) -> mongoose_bin:gen_from_timestamp();
132 107 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 11 Version = exml_query:path(QueryEl, [{element, <<"version">>}, cdata], <<>>),
143 11 {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 12 try parse_config(QueryEls) of
147 {ok, RawConfig} ->
148 12 {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 83 try parse_aff_users(QueryEls) of
158 {ok, AffUsers} ->
159 83 {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 10 try parse_blocking_list(QueryEls) of
173 {ok, BlockingList} ->
174 10 {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 114 ConfigEl = exml_query:path(QueryEl, [{element, <<"configuration">>}]),
179 114 OccupantsEl = exml_query:path(QueryEl, [{element, <<"occupants">>}]),
180 114 try parse_config(safe_get_children(ConfigEl)) of
181 {ok, RawConfig} ->
182 114 try parse_aff_users(safe_get_children(OccupantsEl)) of
183 {ok, AffUsers} ->
184 114 {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 29 {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 126 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 126 {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 34 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 197 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 197 {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 219 #jid{} = JID = jid:from_binary(JIDBin),
236 219 Aff = mod_muc_light_utils:b2aff(AffBin),
237 219 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 10 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 10 {ok, ItemsAcc};
249 parse_blocking_list([#xmlel{ name = WhatBin, attrs = [{<<"action">>, ActionBin}],
250 children = [ #xmlcdata{ content = JIDBin } ] } | RItemsEls],
251 ItemsAcc) ->
252 14 #jid{} = JID = jid:from_binary(JIDBin),
253 14 Action = b2action(ActionBin),
254 14 What = b2what(WhatBin),
255 14 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 10 ConfigEls = [ kv_to_el(Field) || Field <- [{<<"version">>, Config#config.version}
284 | Config#config.raw_config] ],
285 10 {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 198 Attrs = [
310 {<<"id">>, Affs#affiliations.id},
311 {<<"type">>, <<"groupchat">>},
312 {<<"from">>, RoomBin}
313 ],
314
315 198 AllAffsEls = [ aff_user_to_el(AffUser) || AffUser <- Affs#affiliations.aff_users ],
316 198 VersionEl = kv_to_el(<<"version">>, Affs#affiliations.version),
317 198 NotifForCurrentNoPrevVersion = [ VersionEl | AllAffsEls ],
318 198 MsgForArch = #xmlel{ name = <<"message">>, attrs = Attrs,
319 children = msg_envelope(?NS_MUC_LIGHT_AFFILIATIONS,
320 NotifForCurrentNoPrevVersion) },
321 198 EventData = room_event(Acc, RoomJID),
322 198 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
323 198 #xmlel{children = FinalChildrenForCurrentNoPrevVersion}
324 = mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData),
325 198 FinalChildrenForCurrent = inject_prev_version(FinalChildrenForCurrentNoPrevVersion,
326 Affs#affiliations.prev_version),
327 198 bcast_aff_messages(RoomJID, OldAffUsers, NewAffUsers, Attrs, VersionEl,
328 FinalChildrenForCurrent, HandleFun),
329
330 198 {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 9 {reply, ID};
338 encode_iq({set, #create{} = Create, UniqueRequested},
339 _Sender, RoomJID, RoomBin, HandleFun, Acc) ->
340 111 Attrs = [
341 {<<"id">>, Create#create.id},
342 {<<"type">>, <<"groupchat">>},
343 {<<"from">>, RoomBin}
344 ],
345
346 111 VersionEl = kv_to_el(<<"version">>, Create#create.version),
347 111 bcast_aff_messages(RoomJID, [], Create#create.aff_users, Attrs, VersionEl, [], HandleFun),
348
349 111 AllAffsEls = [ aff_user_to_el(AffUser) || AffUser <- Create#create.aff_users ],
350 111 MsgForArch = #xmlel{ name = <<"message">>, attrs = Attrs,
351 children = msg_envelope(?NS_MUC_LIGHT_AFFILIATIONS, AllAffsEls) },
352 111 EventData = room_event(Acc, RoomJID),
353 111 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
354 111 mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData),
355
356 %% IQ reply "from"
357 %% Sent from service JID when unique room was requested
358 111 {ResFromJID, ResFromBin} = case UniqueRequested of
359 7 true -> {#jid{ server = RoomJID#jid.lserver,
360 lserver = RoomJID#jid.lserver },
361 RoomJID#jid.lserver};
362 104 false -> {RoomJID, RoomBin}
363 end,
364 111 {reply, ResFromJID, ResFromBin, <<>>, undefined, Create#create.id};
365 encode_iq({set, #destroy{ id = ID }, AffUsers},
366 _Sender, RoomJID, RoomBin, HandleFun, _Acc) ->
367 6 Attrs = [
368 {<<"id">>, ID},
369 {<<"type">>, <<"groupchat">>},
370 {<<"from">>, RoomBin}
371 ],
372
373 6 lists:foreach(
374 fun({{U, S}, _}) ->
375 15 NoneAffEnveloped = msg_envelope(?NS_MUC_LIGHT_AFFILIATIONS,
376 [aff_user_to_el({{U, S}, none})]),
377 15 DestroyEnveloped = [ #xmlel{ name = <<"x">>,
378 attrs = [{<<"xmlns">>, ?NS_MUC_LIGHT_DESTROY}] }
379 | NoneAffEnveloped ],
380 15 msg_to_aff_user(RoomJID, U, S, Attrs, DestroyEnveloped, HandleFun)
381 end, AffUsers),
382
383 6 {reply, ID};
384 encode_iq({set, #config{} = Config, AffUsers},
385 _Sender, RoomJID, RoomBin, HandleFun, Acc) ->
386 10 MsgForArch = encode_set_config(Config, RoomBin),
387 10 EventData = room_event(Acc, RoomJID),
388 10 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
389 10 #xmlel{ children = FinalConfigNotif }
390 = mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData),
391
392 10 lists:foreach(
393 fun({{U, S}, _}) ->
394 30 msg_to_aff_user(RoomJID, U, S, MsgForArch#xmlel.attrs, FinalConfigNotif, HandleFun)
395 end, AffUsers),
396
397 10 {reply, Config#config.id}.
398
399 encode_set_config(Config, RoomBin) ->
400 10 Attrs = [
401 {<<"id">>, Config#config.id},
402 {<<"type">>, <<"groupchat">>},
403 {<<"from">>, RoomBin}
404 ],
405 10 ConfigEls = [ kv_to_el(ConfigField) || ConfigField <- Config#config.raw_config ],
406 10 ConfigNotif = [ kv_to_el(<<"prev-version">>, Config#config.prev_version),
407 kv_to_el(<<"version">>, Config#config.version)
408 | ConfigEls ],
409 10 #xmlel{name = <<"message">>, attrs = Attrs,
410 children = msg_envelope(?NS_MUC_LIGHT_CONFIGURATION, ConfigNotif) }.
411
412 %% --------------------------- Helpers ---------------------------
413
414 -spec identity() -> mongoose_disco:identity().
415 identity() ->
416 4 #{category => <<"conference">>, type => <<"text">>, name => <<"MUC Light (modern)">>}.
417
418 -spec aff_user_to_el(aff_user()) -> exml:element().
419 aff_user_to_el({User, Aff}) ->
420 953 #xmlel{ name = <<"user">>,
421 attrs = [{<<"affiliation">>, mod_muc_light_utils:aff2b(Aff)}],
422 children = [#xmlcdata{ content = jid:to_binary(User) }] }.
423
424 -spec blocking_to_el(blocking_item()) -> exml:element().
425 blocking_to_el({What, Action, Who}) ->
426 2 #xmlel{ name = what2b(What),
427 attrs = [{<<"action">>, action2b(Action)}],
428 children = [#xmlcdata{ content = jid:to_binary(Who) }] }.
429
430 -spec kv_to_el({binary(), binary()}) -> exml:element().
431 kv_to_el({Key, Value}) ->
432 49 kv_to_el(Key, Value).
433
434 -spec kv_to_el(binary(), binary()) -> exml:element().
435 kv_to_el(Key, Value) ->
436 583 #xmlel{ name = Key, children = [#xmlcdata{ content = Value }] }.
437
438 -spec msg_envelope(XMLNS :: binary(), Children :: [jlib:xmlch()]) -> [exml:element()].
439 msg_envelope(XMLNS, Children) ->
440 770 [ #xmlel{ name = <<"x">>, attrs = [{<<"xmlns">>, XMLNS}], children = Children },
441 #xmlel{ name = <<"body">> } ].
442
443 -spec inject_prev_version(IQChildren :: [jlib:xmlch()], PrevVersion :: binary()) -> [jlib:xmlch()].
444 inject_prev_version([#xmlel{ name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_MUC_LIGHT_AFFILIATIONS}],
445 children = Items} = XEl | REls], PrevVersion) ->
446 198 [XEl#xmlel{ children = [kv_to_el(<<"prev-version">>, PrevVersion) | Items] } | REls];
447 inject_prev_version([El | REls], PrevVersion) ->
448
:-(
[El | inject_prev_version(REls, PrevVersion)].
449
450 -spec bcast_aff_messages(From :: jid:jid(), OldAffUsers :: aff_users(),
451 NewAffUsers :: aff_users(), Attrs :: [{binary(), binary()}],
452 VersionEl :: exml:element(), Children :: [jlib:xmlch()],
453 HandleFun :: mod_muc_light_codec_backend:encoded_packet_handler()) -> ok.
454 bcast_aff_messages(_, [], [], _, _, _, _) ->
455 309 ok;
456 bcast_aff_messages(From, [{User, _} | ROldAffUsers], [], Attrs, VersionEl, Children, HandleFun) ->
457 94 msg_to_leaving_user(From, User, Attrs, HandleFun),
458 94 bcast_aff_messages(From, ROldAffUsers, [], Attrs, VersionEl, Children, HandleFun);
459 bcast_aff_messages(From, [{{ToU, ToS} = User, _} | ROldAffUsers], [{User, _} | RNewAffUsers],
460 Attrs, VersionEl, Children, HandleFun) ->
461 145 msg_to_aff_user(From, ToU, ToS, Attrs, Children, HandleFun),
462 145 bcast_aff_messages(From, ROldAffUsers, RNewAffUsers, Attrs, VersionEl, Children, HandleFun);
463 bcast_aff_messages(From, [{User1, _} | ROldAffUsers], [{User2, _} | _] = NewAffUsers,
464 Attrs, VersionEl, Children, HandleFun) when User1 < User2 ->
465 56 msg_to_leaving_user(From, User1, Attrs, HandleFun),
466 56 bcast_aff_messages(From, ROldAffUsers, NewAffUsers, Attrs, VersionEl, Children, HandleFun);
467 bcast_aff_messages(From, OldAffUsers, [{{ToU, ToS}, _} = AffUser | RNewAffUsers],
468 Attrs, VersionEl, Children, HandleFun) ->
469 286 NotifForNewcomer = msg_envelope(?NS_MUC_LIGHT_AFFILIATIONS,
470 [ VersionEl, aff_user_to_el(AffUser) ]),
471 286 msg_to_aff_user(From, ToU, ToS, Attrs, NotifForNewcomer, HandleFun),
472 286 bcast_aff_messages(From, OldAffUsers, RNewAffUsers, Attrs, VersionEl, Children, HandleFun).
473
474 -spec msg_to_leaving_user(From :: jid:jid(), User :: jid:simple_bare_jid(),
475 Attrs :: [{binary(), binary()}],
476 HandleFun :: mod_muc_light_codec_backend:encoded_packet_handler()) -> ok.
477 msg_to_leaving_user(From, {ToU, ToS} = User, Attrs, HandleFun) ->
478 150 NotifForLeaving = msg_envelope(?NS_MUC_LIGHT_AFFILIATIONS, [ aff_user_to_el({User, none}) ]),
479 150 msg_to_aff_user(From, ToU, ToS, Attrs, NotifForLeaving, HandleFun).
480
481 -spec msg_to_aff_user(From :: jid:jid(), ToU :: jid:luser(), ToS :: jid:lserver(),
482 Attrs :: [{binary(), binary()}], Children :: [jlib:xmlch()],
483 HandleFun :: mod_muc_light_codec_backend:encoded_packet_handler()) -> ok.
484 msg_to_aff_user(From, ToU, ToS, Attrs, Children, HandleFun) ->
485 1353 To = jid:make_noprep(ToU, ToS, <<>>),
486 1353 ToBin = jid:to_binary({ToU, ToS, <<>>}),
487 1353 Packet = #xmlel{ name = <<"message">>, attrs = [{<<"to">>, ToBin} | Attrs],
488 children = Children },
489 1353 HandleFun(From, To, Packet).
490
491 -spec jids_from_room_with_resource(jid:jid(), binary()) ->
492 {jid:jid(), binary()}.
493 jids_from_room_with_resource(RoomJID, Resource) ->
494 674 From = jid:replace_resource(RoomJID, Resource),
495 674 FromBin = jid:to_binary(jid:to_lower(From)),
496 674 {From, FromBin}.
497
498 -spec make_iq_result(FromBin :: binary(), ToBin :: binary(), ID :: binary(),
499 XMLNS :: binary(), Els :: [jlib:xmlch()] | undefined) -> exml:element().
500 make_iq_result(FromBin, ToBin, ID, XMLNS, Els) ->
501 377 Attrs = [
502 {<<"from">>, FromBin},
503 {<<"to">>, ToBin},
504 {<<"id">>, ID},
505 {<<"type">>, <<"result">>}
506 ],
507 377 Query = make_query_el(XMLNS, Els),
508 377 #xmlel{ name = <<"iq">>, attrs = Attrs, children = Query }.
509
510 -spec make_query_el(binary(), [jlib:xmlch()] | undefined) -> [exml:element()].
511 make_query_el(_, undefined) ->
512 337 [];
513 make_query_el(XMLNS, Els) ->
514 40 [#xmlel{ name = <<"query">>, attrs = [{<<"xmlns">>, XMLNS}], children = Els }].
515
516 %%====================================================================
517 %% Common helpers and internal functions
518 %%====================================================================
519
520 -spec safe_get_children(exml:element() | term()) -> [exml:element() | exml:cdata()].
521 228 safe_get_children(#xmlel{ children = Ch }) -> Ch;
522
:-(
safe_get_children(_) -> [].
523
524 -spec b2action(ActionBin :: binary()) -> atom().
525 4 b2action(<<"allow">>) -> allow;
526 10 b2action(<<"deny">>) -> deny.
527
528 -spec action2b(Action :: atom()) -> binary().
529
:-(
action2b(allow) -> <<"allow">>;
530 2 action2b(deny) -> <<"deny">>.
531
532 -spec b2what(WhatBin :: binary()) -> atom().
533 9 b2what(<<"user">>) -> user;
534 5 b2what(<<"room">>) -> room.
535
536 -spec what2b(What :: atom()) -> binary().
537 1 what2b(user) -> <<"user">>;
538 1 what2b(room) -> <<"room">>.
539
540 -spec room_event(mongoose_acc:t(), jid:jid()) -> mod_muc:room_event_data().
541 room_event(Acc, RoomJID) ->
542 319 TS = mongoose_acc:timestamp(Acc),
543 319 #{from_nick => <<>>,
544 from_jid => RoomJID,
545 room_jid => RoomJID,
546 affiliation => owner,
547 role => moderator,
548 timestamp => TS}.
Line Hits Source