./ct_report/coverage/mod_muc_light_codec_legacy.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : mod_muc_light_codec_legacy.erl
3 %%% Author : Piotr Nosek <piotr.nosek@erlang-solutions.com>
4 %%% Purpose : MUC Light codec for XEP-0045 compatibility
5 %%% Created : 27 Oct 2015 by Piotr Nosek <piotr.nosek@erlang-solutions.com>
6 %%%----------------------------------------------------------------------
7
8 -module(mod_muc_light_codec_legacy).
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 %%====================================================================
21 %% API
22 %%====================================================================
23
24 -spec decode(From :: jid:jid(), To :: jid:jid(),
25 Stanza :: jlib:iq() | exml:element(),
26 Acc :: mongoose_acc:t()) -> mod_muc_light_codec_backend:decode_result().
27 decode(_From, #jid{ luser = ToU } = _To, #xmlel{ name = <<"presence">> } = Stanza, _Acc)
28 when ToU =/= <<>> ->
29 5 case {exml_query:path(Stanza, [{element, <<"x">>}, {attr, <<"xmlns">>}]),
30 exml_query:attr(Stanza, <<"type">>)} of
31 {?NS_MUC, Available} when Available =:= undefined orelse
32 3 Available =:= <<"available">> -> {ok, {set, #create{}}};
33 2 _ -> ignore
34 end;
35 decode(_From, #jid{ lresource = Resource }, _Stanza, _Acc) when Resource =/= <<>> ->
36
:-(
{error, bad_request};
37 decode(_From, _To, #xmlel{ name = <<"message">> } = Stanza, _Acc) ->
38 7 decode_message(Stanza);
39 decode(From, _To, #xmlel{ name = <<"iq">> } = Stanza, _Acc) ->
40 55 decode_iq(From, jlib:iq_query_info(Stanza));
41 decode(From, _To, #iq{} = IQ, _Acc) ->
42
:-(
decode_iq(From, IQ);
43 decode(_, _, _, _Acc) ->
44
:-(
{error, bad_request}.
45
46 -spec encode(Request :: muc_light_encode_request(),
47 OriginalSender :: jid:jid(), RoomUS :: jid:jid(),
48 HandleFun :: mod_muc_light_codec_backend:encoded_packet_handler(),
49 Acc :: mongoose_acc:t()) -> mongoose_acc:t().
50 encode({#msg{} = Msg, AffUsers}, Sender, RoomBareJid, HandleFun, Acc) ->
51 3 US = jid:to_lus(Sender),
52 3 Aff = get_sender_aff(AffUsers, US),
53 3 FromNick = jid:to_bare_binary(Sender),
54 3 {RoomJID, RoomBin} = jids_from_room_with_resource(RoomBareJid, FromNick),
55 3 Attrs = [
56 {<<"id">>, Msg#msg.id},
57 {<<"type">>, <<"groupchat">>},
58 {<<"from">>, RoomBin}
59 ],
60 3 MsgForArch = #xmlel{ name = <<"message">>, attrs = Attrs, children = Msg#msg.children },
61 3 TS = mongoose_acc:timestamp(Acc),
62 3 EventData = #{from_nick => FromNick,
63 from_jid => Sender,
64 room_jid => RoomBareJid,
65 affiliation => Aff,
66 role => mod_muc_light_utils:light_aff_to_muc_role(Aff),
67 timestamp => TS},
68 3 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
69 3 Packet1 = #xmlel{ children = Children }
70 = mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData),
71 3 lists:foreach(
72 fun({{U, S}, _}) ->
73 9 send_to_aff_user(RoomJID, U, S, <<"message">>, Attrs, Children, HandleFun)
74 end, AffUsers),
75 3 mongoose_acc:update_stanza(#{from_jid => RoomJID,
76 to_jid => RoomBareJid,
77 element => Packet1}, Acc);
78 encode(OtherCase, Sender, RoomBareJid, HandleFun, Acc) ->
79 50 {RoomJID, RoomBin} = jids_from_room_with_resource(RoomBareJid, <<>>),
80 50 case encode_meta(OtherCase, RoomJID, Sender, HandleFun, Acc) of
81 {iq_reply, ID} ->
82 23 IQRes = make_iq_result(RoomBin, jid:to_binary(Sender), ID, <<>>, undefined),
83 23 HandleFun(RoomJID, Sender, IQRes);
84 {iq_reply, XMLNS, Els, ID} ->
85 22 IQRes = make_iq_result(RoomBin, jid:to_binary(Sender), ID, XMLNS, Els),
86 22 HandleFun(RoomJID, Sender, IQRes);
87 noreply ->
88 5 Acc
89 end.
90
91 -spec encode_error(
92 ErrMsg :: tuple(), OrigFrom :: jid:jid(), OrigTo :: jid:jid(),
93 OrigPacket :: exml:element(), Acc :: mongoose_acc:t()) ->
94 mongoose_acc:t().
95 encode_error(_, OrigFrom, OrigTo, #xmlel{ name = <<"presence">> } = OrigPacket, Acc) ->
96 %% The only error case for valid presence is registration-required for room creation
97 1 X = #xmlel{ name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_MUC}] },
98 1 mod_muc_light_codec_backend:encode_error({error, registration_required}, [X], OrigFrom, OrigTo,
99 OrigPacket, Acc);
100 encode_error(ErrMsg, OrigFrom, OrigTo, OrigPacket, Acc) ->
101 11 mod_muc_light_codec_backend:encode_error(ErrMsg, [], OrigFrom, OrigTo, OrigPacket, Acc).
102
103 %%====================================================================
104 %% Message decoding
105 %%====================================================================
106
107 -spec decode_message(Packet :: exml:element()) ->
108 {ok, muc_light_packet()} | {error, bad_request} | ignore.
109 decode_message(#xmlel{ attrs = Attrs, children = Children }) ->
110 7 decode_message_by_type(lists:keyfind(<<"type">>, 1, Attrs),
111 lists:keyfind(<<"id">>, 1, Attrs), Children).
112
113 -spec decode_message_by_type(Type :: {binary(), binary()} | false,
114 Id :: {binary(), binary()} | false,
115 Children :: [jlib:xmlch()]) ->
116 {ok, msg() | {set, mod_muc_light_room_config:kv()}} | {error, bad_request} | ignore.
117 decode_message_by_type({_, <<"groupchat">>}, _, [#xmlel{ name = <<"subject">> } = SubjectEl]) ->
118 3 {ok, {set, #config{ raw_config = [{<<"subject">>, exml_query:cdata(SubjectEl)}] }}};
119 decode_message_by_type({_, <<"groupchat">>}, Id, Children) ->
120 4 {ok, #msg{ children = Children, id = ensure_id(Id) }};
121 decode_message_by_type({_, <<"error">>}, _, _) ->
122
:-(
ignore;
123 decode_message_by_type(_, _, _) ->
124
:-(
{error, bad_request}.
125
126 -spec ensure_id(Id :: {binary(), binary()} | false) -> binary().
127 1 ensure_id(false) -> mongoose_bin:gen_from_timestamp();
128 3 ensure_id({_, Id}) -> Id.
129
130 %%====================================================================
131 %% IQ decoding
132 %%====================================================================
133
134 -spec decode_iq(From :: jid:jid(), IQ :: jlib:iq()) ->
135 {ok, muc_light_packet() | muc_light_disco() | jlib:iq()} | {error, bad_request} | ignore.
136 decode_iq(_From, #iq{ xmlns = ?NS_MUC_OWNER, type = get, sub_el = _QueryEl, id = ID }) ->
137 9 {ok, {get, #config{ id = ID }}};
138 decode_iq(From, IQ = #iq{ xmlns = ?NS_MUC_OWNER, type = set, sub_el = QueryEl, id = ID }) ->
139 10 case exml_query:subelement(QueryEl, <<"destroy">>) of
140 undefined ->
141 9 case parse_config_form(QueryEl) of
142 {ok, RawConfig} ->
143 6 {ok, {set, #config{ id = ID, raw_config = RawConfig }}};
144 {error, Reason} ->
145 3 ?LOG_WARNING(#{what => muc_parse_config_failed,
146
:-(
from_jid => jid:to_binary(From), iq => IQ, reason => Reason}),
147 3 {error, bad_request}
148 end;
149 _ ->
150 1 {ok, {set, #destroy{ id = ID }}}
151 end;
152 decode_iq(_From, #iq{ xmlns = ?NS_MUC_ADMIN, type = get, sub_el = _QueryEl, id = ID }) ->
153 3 {ok, {get, #affiliations{ id = ID }}};
154 decode_iq(From, IQ = #iq{ xmlns = ?NS_MUC_ADMIN, type = set, sub_el = QueryEl, id = ID }) ->
155 16 try parse_aff_users(exml_query:subelements(QueryEl, <<"item">>)) of
156 {ok, AffUsers} ->
157 16 {ok, {set, #affiliations{ id = ID, aff_users = AffUsers }}}
158 catch Class:Reason:Stacktrace ->
159
:-(
?LOG_WARNING(#{what => muc_parse_aff_users_failed,
160 from_jid => jid:to_binary(From), iq => IQ,
161
:-(
class => Class, reason => Reason, stacktrace => Stacktrace}),
162
:-(
{error, bad_request}
163 end;
164 decode_iq(_From, #iq{ xmlns = ?NS_PRIVACY, type = get, id = ID }) ->
165 4 {ok, {get, #blocking{ id = ID }}};
166 decode_iq(From, IQ = #iq{ xmlns = ?NS_PRIVACY, type = set,
167 sub_el = #xmlel{ children = Lists }, id = ID }) ->
168 5 case lists:keyfind([{<<"name">>, ?NS_MUC_LIGHT}], #xmlel.attrs, Lists) of
169 false ->
170
:-(
ignore;
171 List ->
172 5 try parse_blocking_list(exml_query:subelements(List, <<"item">>)) of
173 {ok, BlockingList} ->
174 5 {ok, {set, #blocking{ id = ID, items = BlockingList }}}
175 catch Class:Reason:Stacktrace ->
176
:-(
?LOG_WARNING(#{what => muc_parse_blocking_list_failed,
177 from_jid => jid:to_binary(From), iq => IQ,
178
:-(
class => Class, reason => Reason, stacktrace => Stacktrace})
179 end
180 end;
181 decode_iq(_From, #iq{ xmlns = ?NS_DISCO_ITEMS, type = get, id = ID} = IQ) ->
182 4 {ok, {get, #disco_items{ id = ID, rsm = jlib:rsm_decode(IQ) }}};
183 decode_iq(_From, #iq{ xmlns = ?NS_DISCO_INFO, type = get, id = ID}) ->
184 4 {ok, {get, #disco_info{ id = ID }}};
185 decode_iq(_From, #iq{ type = error }) ->
186
:-(
ignore;
187 decode_iq(_From, #iq{} = IQ) ->
188
:-(
{ok, IQ}.
189
190 %% ------------------ Parsers ------------------
191
192 -spec parse_config_form(exml:element()) -> {ok, [{binary(), binary()}]} | {error, binary()}.
193 parse_config_form(QueryEl) ->
194 9 case mongoose_data_forms:find_and_parse_form(QueryEl) of
195 #{type := <<"submit">>, kvs := KVs} ->
196 6 {ok, [{K, V} || {K, [V]} <- maps:to_list(KVs)]};
197 #{} ->
198 1 {error, <<"Invalid form type">>};
199 {error, Msg} ->
200 2 {error, Msg}
201 end.
202
203 -spec parse_aff_users(Els :: [jlib:xmlch()]) -> {ok, aff_users()}.
204 parse_aff_users(Els) ->
205 16 parse_aff_users(Els, []).
206
207 -spec parse_aff_users(Els :: [jlib:xmlch()], AffUsersAcc :: aff_users()) -> {ok, aff_users()}.
208 parse_aff_users([], AffUsersAcc) ->
209 16 {ok, AffUsersAcc};
210 parse_aff_users([Item | RItemsEls], AffUsersAcc) ->
211 24 AffBin = exml_query:attr(Item, <<"affiliation">>),
212 24 JIDBin = exml_query:attr(Item, <<"jid">>),
213 24 #jid{} = JID = jid:from_binary(JIDBin),
214 24 Aff = mod_muc_light_utils:b2aff(AffBin),
215 24 parse_aff_users(RItemsEls, [{jid:to_lus(JID), Aff} | AffUsersAcc]).
216
217 -spec parse_blocking_list(Els :: [jlib:xmlch()]) -> {ok, [blocking_item()]}.
218 parse_blocking_list(ItemsEls) ->
219 5 parse_blocking_list(ItemsEls, []).
220
221 -spec parse_blocking_list(Els :: [jlib:xmlch()], ItemsAcc :: [blocking_item()]) ->
222 {ok, [blocking_item()]}.
223 parse_blocking_list([], ItemsAcc) ->
224 5 {ok, ItemsAcc};
225 parse_blocking_list([Item | RItemsEls], ItemsAcc) ->
226 8 JIDBin = exml_query:attr(Item, <<"value">>),
227 8 ActionBin = exml_query:attr(Item, <<"action">>),
228 8 #jid{} = JID = jid:from_binary(JIDBin),
229 8 Action = b2action(ActionBin),
230 8 {What, Who} = case {JID#jid.luser =:= <<>>, JID#jid.lresource =:= <<>>} of
231 {false, true} ->
232 4 {room, JID};
233 {true, false} ->
234 4 {user, jid:from_binary(JID#jid.lresource)}
235 end,
236 8 parse_blocking_list(RItemsEls, [{What, Action, jid:to_lus(Who)} | ItemsAcc]).
237
238 %%====================================================================
239 %% Encoding
240 %%====================================================================
241
242 -spec encode_meta(Request :: muc_light_encode_request(), RoomJID :: jid:jid(),
243 SenderJID :: jid:jid(),
244 HandleFun :: mod_muc_light_codec_backend:encoded_packet_handler(),
245 Acc :: mongoose_acc:t()) ->
246 {iq_reply, ID :: binary()} |
247 {iq_reply, XMLNS :: binary(), Els :: [jlib:xmlch()], ID :: binary()} |
248 noreply.
249 encode_meta({get, #disco_info{ id = ID }}, RoomJID, SenderJID, _HandleFun, Acc) ->
250 4 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
251 4 IdentityXML = mongoose_disco:identities_to_xml([identity()]),
252 4 FeatureXML = mongoose_disco:get_muc_features(HostType, SenderJID, RoomJID, <<>>, <<>>,
253 [?NS_MUC]),
254 4 DiscoEls = IdentityXML ++ FeatureXML,
255 4 {iq_reply, ?NS_DISCO_INFO, DiscoEls, ID};
256 encode_meta({get, #disco_items{ rooms = Rooms, id = ID, rsm = RSMOut }},
257 _RoomJID, _SenderJID, _HandleFun, _Acc) ->
258 3 DiscoEls = [ #xmlel{ name = <<"item">>,
259 attrs = [{<<"jid">>, <<RoomU/binary, $@, RoomS/binary>>},
260 {<<"name">>, RoomName}] }
261 3 || {{RoomU, RoomS}, RoomName, _RoomVersion} <- Rooms ],
262 3 {iq_reply, ?NS_DISCO_ITEMS, jlib:rsm_encode(RSMOut) ++ DiscoEls, ID};
263 encode_meta({get, #config{} = Config}, _RoomJID, _SenderJID, _HandleFun, _Acc) ->
264 9 Fields = [#{var => K, type => <<"text-single">>, values => [V]}
265 9 || {K, V} <- Config#config.raw_config],
266 9 XEl = mongoose_data_forms:form(#{title => <<"Configuration form for the room">>,
267 ns => <<"http://jabber.org/protocol/muc#roomconfig">>,
268 fields => Fields}),
269 9 {iq_reply, ?NS_MUC_OWNER, [XEl], Config#config.id};
270 encode_meta({get, #affiliations{} = Affs}, _RoomJID, _SenderJID, _HandleFun, _Acc) ->
271 3 AffEls = [ aff_user_to_item(AffUser) || AffUser <- Affs#affiliations.aff_users ],
272 3 {iq_reply, ?NS_MUC_ADMIN, AffEls, Affs#affiliations.id};
273 encode_meta({set, #affiliations{} = Affs, OldAffUsers, NewAffUsers},
274 RoomJID, SenderJID, HandleFun, _Acc) ->
275 13 bcast_aff_messages(RoomJID, OldAffUsers, NewAffUsers, SenderJID,
276 Affs#affiliations.aff_users, HandleFun),
277 13 {iq_reply, Affs#affiliations.id};
278 encode_meta({get, #blocking{} = Blocking}, RoomJID, _SenderJID, _HandleFun, Acc) ->
279 3 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
280 3 ServerHost = mod_muc_light_utils:room_jid_to_server_host(RoomJID),
281 3 MUCHost = mongoose_subdomain_utils:get_fqdn(mod_muc_light:subdomain_pattern(HostType), ServerHost),
282 3 BlockingEls = [ blocking_to_el(BlockingItem, MUCHost)
283 3 || BlockingItem <- Blocking#blocking.items ],
284 3 Blocklist = #xmlel{ name = <<"list">>, attrs = [{<<"name">>, ?NS_MUC_LIGHT}],
285 children = BlockingEls },
286 3 {iq_reply, ?NS_PRIVACY, [Blocklist], Blocking#blocking.id};
287 encode_meta({set, #blocking{ id = ID }}, _RoomJID, _SenderJID, _HandleFun, _Acc) ->
288 4 {iq_reply, ID};
289 encode_meta({set, #create{} = Create, _UniqueRequested}, RoomJID, _SenderJID, HandleFun, _Acc) ->
290 2 [{{ToU, ToS}, CreatorAff}] = Create#create.aff_users,
291 2 ToBin = jid:to_binary({ToU, ToS, <<>>}),
292 2 {From, FromBin} = jids_from_room_with_resource(RoomJID, ToBin),
293 2 Attrs = [{<<"from">>, FromBin}],
294 2 {AffBin, RoleBin} = case CreatorAff of
295 1 owner -> {<<"owner">>, <<"moderator">>};
296 1 member -> {<<"member">>, <<"participant">>}
297 end,
298 2 NotifEls = [ #xmlel{ name = <<"item">>,
299 attrs = [{<<"affiliation">>, AffBin}, {<<"role">>, RoleBin}]},
300 status(<<"110">>), status(<<"201">>) ],
301 2 Children = envelope(?NS_MUC_USER, NotifEls),
302
303 2 send_to_aff_user(From, ToU, ToS, <<"presence">>, Attrs, Children, HandleFun),
304 2 noreply;
305 encode_meta({set, #destroy{ id = ID }, AffUsers}, RoomJID, _SenderJID, HandleFun, _Acc) ->
306 1 lists:foreach(
307 fun({{U, S}, _}) ->
308 3 FromJID = jid:replace_resource(RoomJID, jid:to_binary({U, S, <<>>})),
309 3 Attrs = [{<<"from">>, jid:to_binary(FromJID)},
310 {<<"type">>, <<"unavailable">>}],
311 3 Children = [ #xmlel{ name = <<"item">>,
312 attrs = [{<<"affiliation">>, <<"none">>},
313 {<<"role">>, <<"none">>}] },
314 #xmlel{ name = <<"destroy">> } ],
315 3 send_to_aff_user(FromJID, U, S, <<"presence">>, Attrs,
316 envelope(?NS_MUC_USER, Children), HandleFun)
317 end, AffUsers),
318
319 1 {iq_reply, ID};
320 encode_meta({set, #config{ raw_config = [{<<"subject">>, Subject}], id = ID }, AffUsers},
321 RoomJID, _SenderJID, HandleFun, _Acc) ->
322 3 Attrs = [
323 {<<"id">>, ID},
324 {<<"type">>, <<"groupchat">>},
325 {<<"from">>, jid:to_binary(RoomJID)}
326 ],
327 3 SubjectEl = #xmlel{ name = <<"subject">>, children = [ #xmlcdata{ content = Subject } ] },
328 3 lists:foreach(
329 fun({{U, S}, _}) ->
330 9 send_to_aff_user(RoomJID, U, S, <<"message">>, Attrs, [SubjectEl], HandleFun)
331 end, AffUsers),
332 3 noreply;
333 encode_meta({set, #config{} = Config, AffUsers}, RoomJID, _SenderJID, HandleFun, _Acc) ->
334 5 Attrs = [{<<"id">>, Config#config.id},
335 {<<"from">>, jid:to_binary(RoomJID)},
336 {<<"type">>, <<"groupchat">>}],
337 5 ConfigNotif = envelope(?NS_MUC_USER, [status(<<"104">>)]),
338 5 lists:foreach(
339 fun({{U, S}, _}) ->
340 15 send_to_aff_user(RoomJID, U, S, <<"message">>, Attrs, ConfigNotif, HandleFun)
341 end, AffUsers),
342
343 5 {iq_reply, Config#config.id}.
344
345 %% --------------------------- Helpers ---------------------------
346
347 -spec identity() -> mongoose_disco:identity().
348 identity() ->
349 4 #{category => <<"conference">>, type => <<"text">>, name => <<"MUC Light (legacy)">>}.
350
351 -spec aff_user_to_item(aff_user()) -> exml:element().
352 aff_user_to_item({User, Aff}) ->
353 44 UserBin = jid:to_binary(User),
354 44 {RoleBin, NickEl} = case Aff of
355 6 owner -> {<<"moderator">>, [{<<"nick">>, UserBin}]};
356 14 member -> {<<"participant">>, [{<<"nick">>, UserBin}]};
357 24 none -> {<<"none">>, []}
358 end,
359 44 #xmlel{ name = <<"item">>,
360 attrs = [{<<"affiliation">>, mod_muc_light_utils:aff2b(Aff)},
361 {<<"jid">>, UserBin},
362 {<<"role">>, RoleBin} | NickEl] }.
363
364 -spec blocking_to_el(BlockingItem :: blocking_item(), Service :: binary()) -> exml:element().
365 blocking_to_el({What, Action, {WhoU, WhoS}}, Service) ->
366 2 WhoBin = jid:to_binary({WhoU, WhoS, <<>>}),
367 2 Value = case What of
368 1 room -> WhoBin;
369 1 user -> <<Service/binary, $/, WhoBin/binary>>
370 end,
371 2 #xmlel{ name = <<"item">>,
372 attrs = [
373 {<<"type">>, <<"jid">>},
374 {<<"value">>, Value},
375 {<<"action">>, action2b(Action)},
376 {<<"order">>, <<"1">>}
377 ] }.
378
379 -spec envelope(XMLNS :: binary(), Children :: [jlib:xmlch()]) -> [jlib:xmlch()].
380 envelope(XMLNS, Children) ->
381 49 [ #xmlel{ name = <<"x">>, attrs = [{<<"xmlns">>, XMLNS}], children = Children } ].
382
383 -spec bcast_aff_messages(Room :: jid:jid(), OldAffUsers :: aff_users(),
384 NewAffUsers :: aff_users(), SenderJID :: jid:jid(),
385 ChangedAffUsers :: aff_users(),
386 HandleFun :: mod_muc_light_codec_backend:encoded_packet_handler()) -> ok.
387 bcast_aff_messages(_, [], [], _, _, _) ->
388 13 ok;
389 bcast_aff_messages(Room, [{User, _} | ROldAffUsers], [], SenderJID, ChangedAffUsers, HandleFun) ->
390 7 msg_to_leaving_user(Room, User, HandleFun),
391 7 bcast_aff_messages(Room, ROldAffUsers, [], SenderJID, ChangedAffUsers, HandleFun);
392 bcast_aff_messages(Room, [{{ToU, ToS} = User, _} | ROldAffUsers], [{User, _} | RNewAffUsers],
393 SenderJID, ChangedAffUsers, HandleFun) ->
394 15 lists:foreach(
395 fun({{ChangedU, ChangedS}, NewAff} = ChangedAffUser) ->
396 24 ChangedUserBin = jid:to_binary({ChangedU, ChangedS, <<>>}),
397 24 {From, FromBin} = jids_from_room_with_resource(Room,
398 ChangedUserBin),
399 24 Attrs0 = [{<<"from">>, FromBin}],
400 24 ElToEnvelope0 = aff_user_to_item(ChangedAffUser),
401 24 {Attrs, ElsToEnvelope} = case NewAff of
402 13 none -> {[{<<"type">>, <<"unavailable">>} | Attrs0],
403 [ElToEnvelope0, status(<<"321">>)]};
404 11 _ -> {Attrs0, [ElToEnvelope0]}
405 end,
406 24 Children = envelope(?NS_MUC_USER, ElsToEnvelope),
407 24 send_to_aff_user(From, ToU, ToS, <<"presence">>, Attrs, Children, HandleFun)
408 end, ChangedAffUsers),
409 15 bcast_aff_messages(Room, ROldAffUsers, RNewAffUsers, SenderJID, ChangedAffUsers, HandleFun);
410 bcast_aff_messages(Room, [{User1, _} | ROldAffUsers], [{User2, _} | _] = NewAffUsers,
411 SenderJID, ChangedAffUsers, HandleFun) when User1 < User2 ->
412 4 msg_to_leaving_user(Room, User1, HandleFun),
413 4 bcast_aff_messages(Room, ROldAffUsers, NewAffUsers, SenderJID, ChangedAffUsers, HandleFun);
414 bcast_aff_messages(Room, OldAffUsers, [{{ToU, ToS}, _} | RNewAffUsers],
415 SenderJID, ChangedAffUsers, HandleFun) ->
416 4 InviterBin = jid:to_binary({SenderJID#jid.luser, SenderJID#jid.lserver, <<>>}),
417 4 RoomBin = jid:to_binary(jid:to_lower(Room)),
418 4 InviteEl = #xmlel{ name = <<"invite">>,
419 attrs = [{<<"from">>, InviterBin}] },
420 4 NotifForNewcomer = envelope(?NS_MUC_USER, [InviteEl]),
421 4 send_to_aff_user(Room, ToU, ToS, <<"message">>, [{<<"from">>, RoomBin}],
422 NotifForNewcomer, HandleFun),
423 4 bcast_aff_messages(Room, OldAffUsers, RNewAffUsers, SenderJID, ChangedAffUsers, HandleFun).
424
425 -spec msg_to_leaving_user(Room :: jid:jid(), User :: jid:simple_bare_jid(),
426 HandleFun :: mod_muc_light_codec_backend:encoded_packet_handler()) -> ok.
427 msg_to_leaving_user(Room, {ToU, ToS} = User, HandleFun) ->
428 11 UserBin = jid:to_binary({ToU, ToS, <<>>}),
429 11 {From, FromBin} = jids_from_room_with_resource(Room, UserBin),
430 11 Attrs = [{<<"from">>, FromBin},
431 {<<"type">>, <<"unavailable">>}],
432 11 NotifForLeaving = envelope(?NS_MUC_USER, [ aff_user_to_item({User, none}), status(<<"321">>) ]),
433 11 send_to_aff_user(From, ToU, ToS, <<"presence">>, Attrs, NotifForLeaving, HandleFun).
434
435 -spec send_to_aff_user(From :: jid:jid(), ToU :: jid:luser(), ToS :: jid:lserver(),
436 Name :: binary(), Attrs :: [{binary(), binary()}],
437 Children :: [jlib:xmlch()],
438 HandleFun :: mod_muc_light_codec_backend:encoded_packet_handler()) -> ok.
439 send_to_aff_user(From, ToU, ToS, Name, Attrs, Children, HandleFun) ->
440 77 To = jid:make_noprep(ToU, ToS, <<>>),
441 77 ToBin = jid:to_binary({ToU, ToS, <<>>}),
442 77 Packet = #xmlel{ name = Name, attrs = [{<<"to">>, ToBin} | Attrs],
443 children = Children },
444 77 HandleFun(From, To, Packet).
445
446 -spec jids_from_room_with_resource(Room :: jid:jid(), binary()) ->
447 {jid:jid(), binary()}.
448 jids_from_room_with_resource(RoomJID, Resource) ->
449 90 From = jid:replace_resource(RoomJID, Resource),
450 90 FromBin = jid:to_binary(jid:to_lower(From)),
451 90 {From, FromBin}.
452
453 -spec make_iq_result(FromBin :: binary(), ToBin :: binary(), ID :: binary(),
454 XMLNS :: binary(), Els :: [jlib:xmlch()] | undefined) -> exml:element().
455 make_iq_result(FromBin, ToBin, ID, XMLNS, Els) ->
456 45 Attrs = [
457 {<<"from">>, FromBin},
458 {<<"to">>, ToBin},
459 {<<"id">>, ID},
460 {<<"type">>, <<"result">>}
461 ],
462 45 Query = make_query_el(XMLNS, Els),
463 45 #xmlel{ name = <<"iq">>, attrs = Attrs, children = Query }.
464
465 -spec make_query_el(binary(), [jlib:xmlch()] | undefined) -> [exml:element()].
466 make_query_el(_, undefined) ->
467 23 [];
468 make_query_el(XMLNS, Els) ->
469 22 [#xmlel{ name = <<"query">>, attrs = [{<<"xmlns">>, XMLNS}], children = Els }].
470
471 -spec status(Code :: binary()) -> exml:element().
472 33 status(Code) -> #xmlel{ name = <<"status">>, attrs = [{<<"code">>, Code}] }.
473
474 %%====================================================================
475 %% Common helpers and internal functions
476 %%====================================================================
477
478 -spec b2action(ActionBin :: binary()) -> atom().
479 2 b2action(<<"allow">>) -> allow;
480 6 b2action(<<"deny">>) -> deny.
481
482 -spec action2b(Action :: atom()) -> binary().
483
:-(
action2b(allow) -> <<"allow">>;
484 2 action2b(deny) -> <<"deny">>.
485
486 get_sender_aff(Users, US) ->
487 3 case lists:keyfind(US, 1, Users) of
488 3 {US, Aff} -> Aff;
489
:-(
_ -> undefined
490 end.
Line Hits Source