./ct_report/coverage/mod_muc_api.COVER.html

1 %% @doc Provide an interface for frontends (like graphql or ctl) to manage MUC rooms.
2 -module(mod_muc_api).
3
4 -export([get_rooms/4,
5 get_room_config/1,
6 get_room_config/2,
7 get_room_messages/3,
8 get_room_messages/4,
9 modify_room_config/2,
10 modify_room_config/3,
11 get_room_users/1,
12 get_room_users/2,
13 get_room_affiliations/2,
14 get_room_affiliations/3,
15 create_instant_room/4,
16 invite_to_room/4,
17 send_message_to_room/3,
18 send_private_message/4,
19 kick_user_from_room/3,
20 kick_user_from_room/4,
21 set_affiliation/3,
22 set_affiliation/4,
23 enter_room/3,
24 exit_room/2,
25 set_role/4,
26 set_role/3,
27 delete_room/2,
28 delete_room/3]).
29
30 -ignore_xref([get_rooms/4, get_rooms/5, delete_room/3,
31 get_room_config/2, get_room_users/2]).
32
33 -include("jlib.hrl").
34 -include("mongoose_rsm.hrl").
35 -include("mod_muc_room.hrl").
36
37 -type short_room_desc() :: #{jid := jid:jid(),
38 title := binary(),
39 private => boolean(),
40 users_number => non_neg_integer()}.
41
42 -type get_rooms_result() :: {[short_room_desc()], jlib:rsm_out()}.
43
44 -type user_map() ::
45 #{jid := jid:jid() | null,
46 role := mod_muc:role(),
47 nick := mod_muc:nick()}.
48
49 -type room_conf_mod_fun() :: fun((mod_muc_room:config()) -> mod_muc_room:config()).
50
51 -type aff_item() :: {jid:simple_jid(), mod_muc:affiliation()}.
52
53 -export_type([user_map/0, short_room_desc/0]).
54
55 -define(ROOM_DELETED_SUCC_RESULT, {ok, "Room deleted successfully"}).
56 -define(USER_CANNOT_ACCESS_ROOM_RESULT,
57 {not_allowed, "Given user does not have permission to read this room"}).
58 -define(ROOM_NOT_FOUND_RESULT, {room_not_found, "Room not found"}).
59 -define(DELETE_NONEXISTENT_ROOM_RESULT, {room_not_found, "Cannot remove non-existent room"}).
60 -define(USER_NOT_FOUND_RESULT, {user_not_found, "Given user not found"}).
61 -define(MUC_SERVER_NOT_FOUND_RESULT, {muc_server_not_found, "MUC server not found"}).
62 -define(MODERATOR_NOT_FOUND_RESULT, {moderator_not_found, "Moderator user not found"}).
63 -define(MODERATOR_RES_NOT_FOUND_RESULT,
64 {moderator_not_found, "Resource with moderator role not found"}).
65 -define(SET_ROLE_SUCC_RESULT, {ok, "Role set successfully"}).
66
67 -spec get_rooms(jid:lserver(), jid:jid(), non_neg_integer() | undefined,
68 non_neg_integer()) -> get_rooms_result().
69 get_rooms(MUCServer, From, Limit, Index) ->
70 6 {Rooms, RSM} = mod_muc:get_vh_rooms(MUCServer, #rsm_in{max = Limit, index = Index}),
71 6 Rooms2 = lists:filtermap(fun(R) -> room_to_item(R, MUCServer, From) end, Rooms),
72 6 {Rooms2, RSM}.
73
74 -spec get_room_config(jid:jid(), jid:jid()) ->
75 {ok, mod_muc_room:config()} | {not_allowed | room_not_found, iolist()}.
76 get_room_config(RoomJID, UserJID) ->
77 3 case mod_muc_room:is_room_owner(RoomJID, UserJID) of
78 {ok, true} ->
79 1 {ok, Config} = mod_muc_room:get_room_config(RoomJID),
80 1 {ok, Config};
81 {ok, false} ->
82 1 {not_allowed, "Given user does not have permission to read config"};
83 {error, not_found} ->
84 1 ?ROOM_NOT_FOUND_RESULT
85 end.
86
87 -spec get_room_config(jid:jid()) -> {ok, mod_muc_room:config()} | {room_not_found, iolist()}.
88 get_room_config(RoomJID) ->
89 8 case mod_muc_room:get_room_config(RoomJID) of
90 {ok, Config} ->
91 7 {ok, Config};
92 {error, not_found} ->
93 1 ?ROOM_NOT_FOUND_RESULT
94 end.
95
96 -spec create_instant_room(jid:lserver(), binary(), jid:jid(), binary()) ->
97 {ok, short_room_desc()} | {internal | user_not_found | room_not_found, iolist()}.
98 create_instant_room(MUCDomain, Name, OwnerJID, Nick) ->
99 %% Because these stanzas are sent on the owner's behalf through
100 %% the HTTP API, they will certainly receive stanzas as a
101 %% consequence, even if their client(s) did not initiate this.
102 10 case ejabberd_auth:does_user_exist(OwnerJID) of
103 true ->
104 9 BareRoomJID = jid:make_bare(Name, MUCDomain),
105 9 UserRoomJID = jid:make(Name, MUCDomain, Nick),
106 %% Send presence to create a room.
107 9 ejabberd_router:route(OwnerJID, UserRoomJID,
108 presence(OwnerJID, UserRoomJID, undefined)),
109 %% Send IQ set to unlock the room.
110 9 ejabberd_router:route(OwnerJID, BareRoomJID,
111 declination(OwnerJID, BareRoomJID)),
112 9 case verify_room(BareRoomJID, OwnerJID) of
113 ok ->
114 7 {ok, #{jid => BareRoomJID, title => Name, private => false, users_number => 0}};
115 Error ->
116 2 Error
117 end;
118 false ->
119 1 ?USER_NOT_FOUND_RESULT
120 end.
121
122 -spec modify_room_config(jid:jid(), jid:jid(), room_conf_mod_fun()) ->
123 {ok, mod_muc_room:config()} | {not_allowed | room_not_found, iolist()}.
124 modify_room_config(RoomJID, UserJID, Fun) ->
125 3 case mod_muc_room:is_room_owner(RoomJID, UserJID) of
126 {ok, true} ->
127 1 {ok, modify_room_config_raw(RoomJID, Fun)};
128 {ok, false} ->
129 1 {not_allowed, "Given user does not have permission to change the config"};
130 {error, not_found} ->
131 1 ?ROOM_NOT_FOUND_RESULT
132 end.
133
134 -spec modify_room_config(jid:jid(), room_conf_mod_fun()) ->
135 {ok, mod_muc_room:config()} | {room_not_found, iolist()}.
136 modify_room_config(RoomJID, Fun) ->
137 2 case mod_muc:room_jid_to_pid(RoomJID) of
138 {ok, _} ->
139 1 {ok, modify_room_config_raw(RoomJID, Fun)};
140 {error, not_found} ->
141 1 ?ROOM_NOT_FOUND_RESULT
142 end.
143
144 -spec invite_to_room(jid:jid(), jid:jid(), jid:jid(), binary()) ->
145 {ok | internal | room_not_found, iolist()}.
146 invite_to_room(RoomJID, SenderJID, RecipientJID, Reason) ->
147 10 case verify_room(RoomJID, SenderJID) of
148 ok ->
149 6 Attrs = case get_room_config(RoomJID) of
150 {ok, #config{password_protected = true, password = Pass}} ->
151 1 [{<<"password">>, Pass}];
152 _ ->
153 5 []
154 end,
155 %% Direct invitation: i.e. not mediated by MUC room. See XEP 0249.
156 6 X = #xmlel{name = <<"x">>,
157 attrs = [{<<"xmlns">>, ?NS_CONFERENCE},
158 {<<"jid">>, jid:to_binary(RoomJID)},
159 {<<"reason">>, Reason} | Attrs]
160 },
161 6 Invite = message(SenderJID, RecipientJID, <<>>, [X]),
162 6 ejabberd_router:route(SenderJID, RecipientJID, Invite),
163 6 {ok, "Invitation sent successfully"};
164 Error ->
165 4 Error
166 end.
167
168 -spec send_message_to_room(jid:jid(), jid:jid(), binary()) -> {ok, iolist()}.
169 send_message_to_room(RoomJID, SenderJID, Message) ->
170 5 Body = #xmlel{name = <<"body">>,
171 children = [#xmlcdata{content = Message}]},
172 5 Stanza = message(SenderJID, RoomJID, <<"groupchat">>, [Body]),
173 5 ejabberd_router:route(SenderJID, RoomJID, Stanza),
174 5 {ok, "Message sent successfully"}.
175
176 -spec send_private_message(jid:jid(), jid:jid(), binary(), binary()) -> {ok, iolist()}.
177 send_private_message(RoomJID, SenderJID, ToNick, Message) ->
178 2 RoomJIDRes = jid:replace_resource(RoomJID, ToNick),
179 2 Body = #xmlel{name = <<"body">>,
180 children = [#xmlcdata{content = Message}]},
181 2 X = #xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_MUC}]},
182 2 Stanza = message(SenderJID, RoomJID, <<"chat">>, [Body, X]),
183 2 ejabberd_router:route(SenderJID, RoomJIDRes, Stanza),
184 2 {ok, "Message sent successfully"}.
185
186 -spec kick_user_from_room(jid:jid(), binary(), binary()) ->
187 {ok | room_not_found | moderator_not_found, iolist()}.
188 kick_user_from_room(RoomJID, Nick, ReasonIn) ->
189 %% All the machinery which is already deeply embedded in the MUC
190 %% modules will perform the neccessary checking.
191 4 case room_moderator(RoomJID) of
192 {ok, SenderJID} ->
193 2 kick_user_from_room_raw(RoomJID, SenderJID, Nick, ReasonIn);
194 {error, moderator_not_found} ->
195 1 ?MODERATOR_NOT_FOUND_RESULT;
196 {error, not_found} ->
197 1 ?ROOM_NOT_FOUND_RESULT
198 end.
199
200 -spec kick_user_from_room(jid:jid(), jid:jid(), binary(), binary()) ->
201 {ok | room_not_found | moderator_not_found, iolist()}.
202 kick_user_from_room(RoomJID, UserJID, Nick, ReasonIn) ->
203 %% All the machinery which is already deeply embedded in the MUC
204 %% modules will perform the neccessary checking.
205 3 case try_add_role_resource(RoomJID, UserJID, moderator) of
206 {ok, #jid{lresource = <<>>}} ->
207 1 ?MODERATOR_RES_NOT_FOUND_RESULT;
208 {ok, SenderJID} ->
209 1 kick_user_from_room_raw(RoomJID, SenderJID, Nick, ReasonIn);
210 {error, not_found} ->
211 1 ?ROOM_NOT_FOUND_RESULT
212 end.
213
214 -spec delete_room(jid:jid(), binary()) -> {ok | room_not_found, iolist()}.
215 delete_room(RoomJID, Reason) ->
216 3 case mod_muc_room:delete_room(RoomJID, Reason) of
217 ok ->
218 1 ?ROOM_DELETED_SUCC_RESULT;
219 {error, not_found} ->
220 2 ?DELETE_NONEXISTENT_ROOM_RESULT
221 end.
222
223 -spec delete_room(jid:jid(), jid:jid(), binary()) ->
224 {ok | not_allowed | room_not_found, iolist()}.
225 delete_room(RoomJID, OwnerJID, Reason) ->
226 3 case mod_muc_room:is_room_owner(RoomJID, OwnerJID) of
227 {ok, true} ->
228 1 ok = mod_muc_room:delete_room(RoomJID, Reason),
229 1 ?ROOM_DELETED_SUCC_RESULT;
230 {ok, false} ->
231 1 {not_allowed, "Given user does not have permission to delete this room"};
232 {error, not_found} ->
233 1 ?DELETE_NONEXISTENT_ROOM_RESULT
234 end.
235 -spec get_room_users(jid:jid()) -> {ok, [user_map()]} | {room_not_found, iolist()}.
236 get_room_users(RoomJID) ->
237 21 case mod_muc_room:get_room_users(RoomJID) of
238 {ok, Users} ->
239 20 {ok, [to_user_map(U, true) || U <- Users]};
240 {error, not_found} ->
241 1 ?ROOM_NOT_FOUND_RESULT
242 end.
243
244 -spec get_room_users(jid:jid(), jid:jid()) ->
245 {ok, [user_map()]} | {not_allowed | room_not_found, iolist()}.
246 get_room_users(RoomJID, UserJID) ->
247 4 case mod_muc_room:can_access_room(RoomJID, UserJID) of
248 {ok, true} ->
249 2 {ok, Users} = mod_muc_room:get_room_users(RoomJID),
250 2 {ok, WithJID} = mod_muc_room:can_access_identity(RoomJID, UserJID),
251 2 {ok, [to_user_map(U, WithJID) || U <- Users]};
252 {ok, false} ->
253 1 ?USER_CANNOT_ACCESS_ROOM_RESULT;
254 {error, not_found} ->
255 1 ?ROOM_NOT_FOUND_RESULT
256 end.
257
258 -spec get_room_messages(jid:jid(), integer() | undefined,
259 mod_mam:unix_timestamp() | undefined) ->
260 {ok, [mod_mam:message_row()]} | {muc_server_not_found | internal, iolist()}.
261 get_room_messages(RoomJID, PageSize, Before) ->
262 1 case mongoose_domain_api:get_subdomain_host_type(RoomJID#jid.lserver) of
263 {ok, HostType} ->
264
:-(
mod_muc_light_api:get_room_messages(HostType, RoomJID, undefined, PageSize, Before);
265 {error, not_found} ->
266 1 ?MUC_SERVER_NOT_FOUND_RESULT
267 end.
268
269 -spec get_room_messages(jid:jid(), jid:jid(), integer() | undefined,
270 mod_mam:unix_timestamp() | undefined) ->
271 {ok, list()} | {muc_server_not_found | room_not_found | internal | not_allowed, iolist()}.
272 get_room_messages(RoomJID, UserJID, PageSize, Before) ->
273 3 case mongoose_domain_api:get_subdomain_host_type(RoomJID#jid.lserver) of
274 {ok, HostType} ->
275 2 case mod_muc_room:can_access_room(RoomJID, UserJID) of
276 {ok, true} ->
277
:-(
mod_muc_light_api:get_room_messages(HostType, RoomJID, UserJID,
278 PageSize, Before);
279 {ok, false} ->
280 1 ?USER_CANNOT_ACCESS_ROOM_RESULT;
281 {error, not_found} ->
282 1 ?ROOM_NOT_FOUND_RESULT
283 end;
284 {error, not_found} ->
285 1 ?MUC_SERVER_NOT_FOUND_RESULT
286 end.
287
288 -spec get_room_affiliations(jid:jid(), jid:jid(), mod_muc:affiliation() | undefined) ->
289 {ok, [aff_item()]} | {not_allowed | room_not_found, iolist()}.
290 get_room_affiliations(RoomJID, UserJID, AffType) ->
291 5 case mod_muc_room:can_access_room(RoomJID, UserJID) of
292 {ok, true} ->
293 3 get_room_affiliations(RoomJID, AffType);
294 {ok, false} ->
295 1 ?USER_CANNOT_ACCESS_ROOM_RESULT;
296 {error, not_found} ->
297 1 ?ROOM_NOT_FOUND_RESULT
298 end.
299
300 -spec get_room_affiliations(jid:jid(), mod_muc:affiliation() | undefined) ->
301 {ok, [aff_item()]} | {room_not_found, iolist()}.
302 get_room_affiliations(RoomJID, AffType) ->
303 20 case room_users_aff(RoomJID) of
304 {ok, Affiliations} ->
305 19 Res = filter_affs_by_type(AffType, Affiliations),
306 19 {ok, Res};
307 {error, not_found} ->
308 1 ?ROOM_NOT_FOUND_RESULT
309 end.
310
311 -spec set_affiliation(jid:jid(), jid:jid(), mod_muc:affiliation()) ->
312 {ok | room_not_found | not_allowed, iolist()}.
313 set_affiliation(RoomJID, UserJID, Affiliation) ->
314 7 case room_users_aff(RoomJID) of
315 {ok, Affs} ->
316 6 {OwnerJID, owner} = hd(filter_affs_by_type(owner, Affs)),
317 6 set_affiliation(RoomJID, OwnerJID, UserJID, Affiliation);
318 {error, not_found} ->
319 1 ?ROOM_NOT_FOUND_RESULT
320 end.
321
322 -spec set_affiliation(jid:jid(), jid:jid(), jid:jid(), mod_muc:affiliation()) ->
323 {ok | room_not_found | not_allowed, iolist()}.
324 set_affiliation(RoomJID, FromJID, UserJID, Affiliation) ->
325 26 AffItem = affiliation_item(UserJID, Affiliation),
326 26 case mod_muc_room:set_admin_items(RoomJID, FromJID, [AffItem]) of
327 ok ->
328 18 {ok, "Affiliation set successfully"};
329 {error, not_found} ->
330 1 ?ROOM_NOT_FOUND_RESULT;
331 {error, #xmlel{} = E} ->
332 7 format_xml_error(E, Affiliation)
333 end.
334
335 -spec set_role(jid:jid(), binary(), mod_muc:role()) ->
336 {ok | moderator_not_found | room_not_found | not_allowed | cannot_modify, iolist()}.
337 set_role(RoomJID, Nick, Role) ->
338 6 case room_moderator(RoomJID) of
339 {ok, ModJID} ->
340 4 case mod_muc_room:set_admin_items(RoomJID, ModJID, [role_item(Nick, Role)]) of
341 ok ->
342 3 ?SET_ROLE_SUCC_RESULT;
343 {error, #xmlel{} = E} ->
344 1 format_xml_error(E, Role)
345 end;
346 {error, moderator_not_found} ->
347 1 ?MODERATOR_NOT_FOUND_RESULT;
348 {error, not_found} ->
349 1 ?ROOM_NOT_FOUND_RESULT
350 end.
351
352 -spec set_role(jid:jid(), jid:jid(), binary(), mod_muc:role()) ->
353 {ok | moderator_not_found | room_not_found | not_allowed | cannot_modify, iolist()}.
354 set_role(RoomJID, UserJID, Nick, Role) ->
355 7 RoleItem = role_item(Nick, Role),
356 7 case try_add_role_resource(RoomJID, UserJID, moderator) of
357 {ok, ModJID} ->
358 6 case mod_muc_room:set_admin_items(RoomJID, ModJID, [RoleItem]) of
359 ok ->
360 4 ?SET_ROLE_SUCC_RESULT;
361 {error, #xmlel{} = E} ->
362 2 format_xml_error(E, Role)
363 end;
364 {error, not_found} ->
365 1 ?ROOM_NOT_FOUND_RESULT
366 end.
367
368 -spec enter_room(jid:jid(), jid:jid(), binary() | undefined) -> {ok, iolist()}.
369 enter_room(RoomJID, UserJID, Password) ->
370 8 Presence = presence(UserJID, RoomJID, Password),
371 8 ejabberd_router:route(UserJID, RoomJID, Presence),
372 8 {ok, "Entering room message sent successfully"}.
373
374 -spec exit_room(jid:jid(), jid:jid()) -> {ok, iolist()}.
375 exit_room(RoomJID, UserJID) ->
376 2 Presence = exit_room_presence(UserJID, RoomJID),
377 2 ejabberd_router:route(UserJID, RoomJID, Presence),
378 2 {ok, "Exiting room message sent successfully"}.
379
380 %% Internal
381
382 -spec kick_user_from_room_raw(jid:jid(), jid:jid(), binary(), binary()) -> {ok, iolist()}.
383 kick_user_from_room_raw(RoomJID, ModJID, Nick, ReasonIn) ->
384 3 Reason = #xmlel{name = <<"reason">>,
385 children = [#xmlcdata{content = ReasonIn}]
386 },
387 3 Item = #xmlel{name = <<"item">>,
388 attrs = [{<<"nick">>, Nick},
389 {<<"role">>, <<"none">>}],
390 children = [ Reason ]
391 },
392 3 IQ = iq(<<"set">>, ModJID, RoomJID, [ query(?NS_MUC_ADMIN, [ Item ]) ]),
393 3 ejabberd_router:route(ModJID, RoomJID, IQ),
394 3 {ok, "Kick message sent successfully"}.
395
396 -spec try_add_role_resource(jid:jid(), jid:jid(), mod_muc:role()) ->
397 {ok, jid:jid()} | {error, not_found}.
398 try_add_role_resource(RoomJID, InUserJID, Role) ->
399 10 case mod_muc_room:get_room_users(RoomJID) of
400 {ok, Users} ->
401 8 case [UserJID || #user{jid = UserJID, role = Role2} <- Users,
402 15 jid:are_bare_equal(InUserJID, UserJID), Role =:= Role2] of
403 [UserJID | _] ->
404 4 {ok, UserJID};
405 _ ->
406 4 {ok, InUserJID}
407 end;
408 Error ->
409 2 Error
410 end.
411
412 5 filter_affs_by_type(undefined, Affs) -> Affs;
413 20 filter_affs_by_type(Type, Affs) -> [Aff || Aff = {_, T} <- Affs, T =:= Type].
414
415 format_xml_error(#xmlel{name = <<"error">>} = E, Value) ->
416 10 case unwrap_xml_error(E) of
417 {<<"406">>, <<"modify">>, <<"not-acceptable">>} ->
418 1 Msg = xml:get_path_s(E, [{elem, <<"text">>}, cdata]),
419 1 {cannot_modify, Msg};
420 {<<"403">>, <<"auth">>, <<"forbidden">>} ->
421 6 {not_allowed, no_permission_msg("affiliation", Value)};
422 {<<"405">>, <<"cancel">>, <<"not-allowed">>} ->
423 3 {not_allowed, no_permission_msg("role", Value)}
424 end.
425
426 no_permission_msg(Op, Value) ->
427 9 io_lib:format("Given user does not have permission to set the ~p ~s", [Value, Op]).
428
429 unwrap_xml_error(#xmlel{attrs = [{<<"code">>, Code}, {<<"type">>, Type}],
430 children = [#xmlel{name = Condition} | _]}) ->
431 10 {Code, Type, Condition}.
432
433 -spec modify_room_config_raw(jid:jid(), room_conf_mod_fun()) -> mod_muc_room:config().
434 modify_room_config_raw(RoomJID, Fun) ->
435 2 {ok, Config} = mod_muc_room:get_room_config(RoomJID),
436 2 {ok, NewConfig2} = mod_muc_room:change_room_config(RoomJID, Fun(Config)),
437 2 NewConfig2.
438
439 -spec to_user_map(mod_muc_room:user(), boolean()) -> user_map().
440 to_user_map(#user{role = Role, nick = Nick}, false = _WithJID) ->
441 2 #{jid => null, role => Role, nick => Nick};
442 to_user_map(#user{jid = JID, role = Role, nick = Nick}, true) ->
443 30 #{jid => JID, role => Role, nick => Nick}.
444
445 -spec room_to_item(tuple(), jid:lserver(), jid:jid()) -> {true, short_room_desc()} | false.
446 room_to_item({{Name, _}, Pid}, MUCServer, From) ->
447 63 case catch gen_fsm_compat:sync_send_all_state_event(
448 Pid, {get_disco_item, From, <<"en">>}, 100) of
449 {item, Desc} ->
450 60 Map = room_desc_to_map(Desc),
451 60 {true, Map#{jid => jid:to_binary({Name, MUCServer, <<>>})}};
452 _ ->
453 3 false
454 end.
455
456 -spec room_desc_to_map(binary()) -> #{title := binary(), private => boolean(),
457 users_number => non_neg_integer()}.
458 room_desc_to_map(Desc) ->
459 60 MP = "(\\S+)( \\((private, )?(\\d+)\\))?",
460 60 {match, [TitlePos, _, PrivatePos, NumberPos]} = re:run(Desc, MP, [{capture, [1, 2, 3, 4]}]),
461 60 Title = binary:part(Desc, TitlePos),
462 60 case NumberPos of
463 {-1, 0} ->
464 1 #{title => Title};
465 _ ->
466 59 Private = {-1, 0} =/= PrivatePos,
467 59 Number = binary_to_integer(binary:part(Desc, NumberPos)),
468 59 #{title => Title, private => Private, users_number => Number}
469 end.
470
471 -spec verify_room(jid:jid(), jid:jid()) -> ok | {internal | not_found, term()}.
472 verify_room(BareRoomJID, OwnerJID) ->
473 19 case mod_muc_room:can_access_room(BareRoomJID, OwnerJID) of
474 {ok, true} ->
475 13 ok;
476 {ok, false} ->
477
:-(
{internal, "Room is locked"};
478 {error, not_found} ->
479 6 ?ROOM_NOT_FOUND_RESULT
480 end.
481
482 role_item(Nick, Role) ->
483 11 #xmlel{name = <<"item">>, attrs = [{<<"nick">>, Nick}, {<<"role">>, atom_to_binary(Role)}]}.
484
485 affiliation_item(JID, Aff) ->
486 26 #xmlel{name = <<"item">>, attrs = [{<<"jid">>, jid:to_binary(JID)},
487 {<<"affiliation">>, atom_to_binary(Aff)}]}.
488
489 iq(Type, Sender, Recipient, Children) when is_binary(Type), is_list(Children) ->
490 12 Addresses = address_attributes(Sender, Recipient),
491 12 #xmlel{name = <<"iq">>,
492 attrs = Addresses ++ [{<<"type">>, Type}],
493 children = Children
494 }.
495
496 message(Sender, Recipient, Type, Contents) when is_binary(Type), is_list(Contents) ->
497 13 Addresses = address_attributes(Sender, Recipient),
498 13 Attributes = case Type of
499 6 <<>> -> Addresses;
500 7 _ -> [{<<"type">>, Type} | Addresses]
501 end,
502 13 #xmlel{name = <<"message">>,
503 attrs = Attributes,
504 children = Contents}.
505
506 query(XMLNameSpace, Children)
507 when is_binary(XMLNameSpace), is_list(Children) ->
508 12 #xmlel{name = <<"query">>,
509 attrs = [{<<"xmlns">>, XMLNameSpace}],
510 children = Children}.
511
512 presence(Sender, Recipient, Password) ->
513 17 Children = case Password of
514 13 undefined -> [];
515 4 _ -> [#xmlel{name = <<"password">>,
516 children = [#xmlcdata{content = Password}]}]
517 end,
518 17 #xmlel{name = <<"presence">>,
519 attrs = address_attributes(Sender, Recipient),
520 children = [#xmlel{name = <<"x">>,
521 attrs = [{<<"xmlns">>, ?NS_MUC}],
522 children = Children}]}.
523
524 exit_room_presence(Sender, Recipient) ->
525 2 #xmlel{name = <<"presence">>,
526 attrs = [{<<"type">>, <<"unavailable">>} | address_attributes(Sender, Recipient)],
527 children = [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_MUC}]}]}.
528
529 declination(Sender, Recipient) ->
530 9 iq(<<"set">>, Sender, Recipient, [data_submission()]).
531
532 data_submission() ->
533 9 query(?NS_MUC_OWNER, [#xmlel{name = <<"x">>,
534 attrs = [{<<"xmlns">>, ?NS_XDATA},
535 {<<"type">>, <<"submit">>}]}]).
536
537 address_attributes(Sender, Recipient) ->
538 44 [{<<"from">>, jid:to_binary(jid:to_lower(Sender))},
539 {<<"to">>, jid:to_binary(jid:to_lower(Recipient))}].
540
541 room_moderator(RoomJID) ->
542 10 case mod_muc_room:get_room_users(RoomJID) of
543 {ok, Users} ->
544 8 case [UserJID || #user{jid = UserJID, role = moderator} <- Users] of
545 6 [ModJID | _] -> {ok, ModJID};
546 2 [] -> {error, moderator_not_found}
547 end;
548 Error ->
549 2 Error
550 end.
551
552 room_users_aff(RoomJID) ->
553 27 case mod_muc_room:get_room_affiliations(RoomJID) of
554 {ok, Affs} ->
555 25 {ok, format_affs(Affs)};
556 {error, Error} ->
557 2 {error, Error}
558 end.
559
560 format_affs(AffsMap) ->
561 25 lists:map(fun
562 20 ({K, {V, <<>>}}) -> {K, V};
563 25 ({K, V}) -> {K, V}
564 end, maps:to_list(AffsMap)).
Line Hits Source