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