./ct_report/coverage/mod_muc_light_commands.COVER.html

1 %%==============================================================================
2 %% Copyright 2016 Erlang Solutions Ltd.
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15 %%
16 %% Author: Joseph Yiasemides <joseph.yiasemides@erlang-solutions.com>
17 %% Description: Administration commands for MUC Light
18 %%==============================================================================
19
20 -module(mod_muc_light_commands).
21
22 -behaviour(gen_mod).
23 -behaviour(mongoose_module_metrics).
24
25 -export([start/2, stop/1, supported_features/0]).
26
27 -export([create_unique_room/4]).
28 -export([create_identifiable_room/5]).
29 -export([send_message/4]).
30 -export([invite_to_room/4]).
31 -export([change_affiliation/5]).
32 -export([delete_room/3]).
33 -export([change_room_config/5]).
34
35 -ignore_xref([delete_room/3, invite_to_room/4, send_message/4]).
36
37 -include("mod_muc_light.hrl").
38 -include("mongoose.hrl").
39 -include("jlib.hrl").
40
41
42 %%--------------------------------------------------------------------
43 %% `gen_mod' callbacks
44 %%--------------------------------------------------------------------
45
46 start(_, _) ->
47 308 mongoose_commands:register(commands()).
48
49 stop(_) ->
50 308 mongoose_commands:unregister(commands()).
51
52 -spec supported_features() -> [atom()].
53 supported_features() ->
54 142 [dynamic_domains].
55
56 %%--------------------------------------------------------------------
57 %% Interface descriptions
58 %%--------------------------------------------------------------------
59
60 commands() ->
61
62 616 [
63 [{name, create_muc_light_room},
64 {category, <<"muc-lights">>},
65 {desc, <<"Create a MUC Light room with unique username part in JID.">>},
66 {module, ?MODULE},
67 {function, create_unique_room},
68 {action, create},
69 {identifiers, [domain]},
70 {args,
71 [
72 %% The parent `domain' under which MUC Light is
73 %% configured.
74 {domain, binary},
75 {name, binary},
76 {owner, binary},
77 {subject, binary}
78 ]},
79 {result, {name, binary}}],
80
81 [{name, create_identifiable_muc_light_room},
82 {category, <<"muc-lights">>},
83 {desc, <<"Create a MUC Light room with user-provided username part in JID">>},
84 {module, ?MODULE},
85 {function, create_identifiable_room},
86 {action, update},
87 {identifiers, [domain]},
88 {args,
89 [{domain, binary},
90 {id, binary},
91 {name, binary},
92 {owner, binary},
93 {subject, binary}
94 ]},
95 {result, {id, binary}}],
96
97 [{name, change_muc_light_room_configuration},
98 {category, <<"muc-lights">>},
99 {subcategory, <<"config">>},
100 {desc, <<"Change configuration of MUC Light room.">>},
101 {module, ?MODULE},
102 {function, change_room_config},
103 {action, update},
104 {identifiers, [domain]},
105 {args,
106 [
107 {domain, binary},
108 {id, binary},
109 {name, binary},
110 {user, binary},
111 {subject, binary}
112 ]},
113 {result, ok}],
114
115 [{name, invite_to_room},
116 {category, <<"muc-lights">>},
117 {subcategory, <<"participants">>},
118 {desc, <<"Invite to a MUC Light room.">>},
119 {module, ?MODULE},
120 {function, invite_to_room},
121 {action, create},
122 {identifiers, [domain, name]},
123 {args,
124 [{domain, binary},
125 {name, binary},
126 {sender, binary},
127 {recipient, binary}
128 ]},
129 {result, ok}],
130
131 [{name, send_message_to_muc_light_room},
132 {category, <<"muc-lights">>},
133 {subcategory, <<"messages">>},
134 {desc, <<"Send a message to a MUC Light room.">>},
135 {module, ?MODULE},
136 {function, send_message},
137 {action, create},
138 {identifiers, [domain, name]},
139 {args,
140 [{domain, binary},
141 {name, binary},
142 {from, binary},
143 {body, binary}
144 ]},
145 {result, ok}],
146
147 [{name, delete_room},
148 {category, <<"muc-lights">>},
149 {subcategory, <<"management">>},
150 {desc, <<"Delete a MUC Light room.">>},
151 {module, ?MODULE},
152 {function, delete_room},
153 {action, delete},
154 {identifiers, [domain, name, owner]},
155 {args,
156 [{domain, binary},
157 {name, binary},
158 {owner, binary}]},
159 {result, ok}]
160 ].
161
162
163 %%--------------------------------------------------------------------
164 %% Internal procedures
165 %%--------------------------------------------------------------------
166
167 create_unique_room(Domain, RoomName, Creator, Subject) ->
168 19 create_room(Domain, <<>>, RoomName, Creator, Subject).
169
170 create_identifiable_room(Domain, Identifier, RoomName, Creator, Subject) ->
171 7 create_room(Domain, Identifier, RoomName, Creator, Subject).
172
173 invite_to_room(Domain, RoomName, Sender, Recipient0) ->
174 1 Recipient1 = jid:binary_to_bare(Recipient0),
175 1 case muc_light_room_name_to_jid_and_aff(jid:from_binary(Sender), RoomName, Domain) of
176 {ok, R, _Aff} ->
177 1 S = jid:binary_to_bare(Sender),
178 1 Changes = query(?NS_MUC_LIGHT_AFFILIATIONS,
179 [affiliate(jid:to_binary(Recipient1), <<"member">>)]),
180 1 ejabberd_router:route(S, R, iq(jid:to_binary(S), jid:to_binary(R),
181 <<"set">>, [Changes]));
182 {error, given_user_does_not_occupy_any_room} ->
183
:-(
{error, forbidden, "given user does not occupy any room"};
184 {error, not_found} ->
185
:-(
{error, not_found, "room does not exist"}
186 end.
187
188 change_affiliation(Domain, RoomID, Sender, Recipient0, Affiliation) ->
189 15 Recipient1 = jid:binary_to_bare(Recipient0),
190 15 LServer = jid:nameprep(Domain),
191 15 HostType = mod_muc_light_utils:server_host_to_host_type(LServer),
192 15 MUCLightDomain = mod_muc_light_utils:server_host_to_muc_host(HostType, LServer),
193 15 R = jid:make(RoomID, MUCLightDomain, <<>>),
194 15 S = jid:binary_to_bare(Sender),
195 15 Changes = query(?NS_MUC_LIGHT_AFFILIATIONS,
196 [affiliate(jid:to_binary(Recipient1), Affiliation)]),
197 15 ejabberd_router:route(S, R, iq(jid:to_binary(S), jid:to_binary(R),
198 <<"set">>, [Changes])).
199
200 change_room_config(Domain, RoomID, RoomName, User, Subject) ->
201 3 LServer = jid:nameprep(Domain),
202 3 HostType = mod_muc_light_utils:server_host_to_host_type(LServer),
203 3 MUCLightDomain = mod_muc_light_utils:server_host_to_muc_host(HostType, LServer),
204 3 UserUS = jid:binary_to_bare(User),
205 3 ConfigReq = #config{ raw_config =
206 [{<<"roomname">>, RoomName}, {<<"subject">>, Subject}]},
207 3 Acc = mongoose_acc:new(#{location => ?LOCATION, lserver => LServer, host_type => HostType}),
208 3 case mod_muc_light:change_room_config(UserUS, RoomID, MUCLightDomain, ConfigReq, Acc) of
209 {ok, _RoomJID, _} ->
210 2 ok;
211 {error, Reason} ->
212 1 {error, internal, Reason}
213 end.
214
215 send_message(Domain, RoomName, Sender, Message) ->
216 1 Body = #xmlel{name = <<"body">>,
217 children = [ #xmlcdata{ content = Message } ]
218 },
219 1 Stanza = #xmlel{name = <<"message">>,
220 attrs = [{<<"type">>, <<"groupchat">>}],
221 children = [ Body ]
222 },
223 1 S = jid:binary_to_bare(Sender),
224 1 case get_user_rooms(jid:to_lus(S), Domain) of
225 [] ->
226
:-(
{error, denied, "given user does not occupy any room"};
227 RoomJIDs when is_list(RoomJIDs) ->
228 1 FindFun = find_room_and_user_aff_by_room_name(RoomName, jid:to_lus(S)),
229 1 {ok, {RU, RS}, _Aff} = lists:foldl(FindFun, none, RoomJIDs),
230 1 true = is_subdomain(RS, Domain),
231 1 R = jid:make(RU, RS, <<>>),
232 1 ejabberd_router:route(S, R, Stanza)
233 end.
234
235 -spec delete_room(DomainName :: binary(), RoomName :: binary(),
236 Owner :: binary()) ->
237 ok | {error, atom(), term()}.
238 delete_room(DomainName, RoomName, Owner) ->
239 4 OwnerJID = jid:binary_to_bare(Owner),
240 4 case muc_light_room_name_to_jid_and_aff(OwnerJID, RoomName, DomainName) of
241 1 {ok, RoomJID, owner} -> mod_muc_light:delete_room(jid:to_lus(RoomJID));
242 1 {ok, _, _} -> {error, denied, "you can not delete this room"};
243 1 {error, given_user_does_not_occupy_any_room} -> {error, denied, "given user does not occupy this room"};
244 1 {error, not_found} -> {error, not_found, "room does not exist"}
245 end.
246
247 %%--------------------------------------------------------------------
248 %% Ancillary
249 %%--------------------------------------------------------------------
250
251 create_room(Domain, RoomId, RoomTitle, Creator, Subject) ->
252 26 LServer = jid:nameprep(Domain),
253 26 HostType = mod_muc_light_utils:server_host_to_host_type(LServer),
254 26 MUCLightDomain = mod_muc_light_utils:server_host_to_muc_host(HostType, LServer),
255 26 CreatorJid = jid:from_binary(Creator),
256 26 MUCServiceJID = jid:make(RoomId, MUCLightDomain, <<>>),
257 26 Config = make_room_config(RoomTitle, Subject),
258 26 case mod_muc_light:try_to_create_room(CreatorJid, MUCServiceJID, Config) of
259 {ok, RoomJid, _} ->
260 25 jid:to_binary(RoomJid);
261 {error, exists} ->
262 1 {error, denied, "Room already exists"};
263 {error, Reason} ->
264
:-(
{error, internal, Reason}
265 end.
266
267 make_room_config(Name, Subject) ->
268 26 #create{raw_config = [{<<"roomname">>, Name},
269 {<<"subject">>, Subject}]
270 }.
271
272 -spec muc_light_room_name_to_jid_and_aff(UserJID :: jid:jid(),
273 RoomName :: binary(),
274 Domain :: jid:lserver()) ->
275 {ok, jid:jid(), aff()} | {error, given_user_does_not_occupy_any_room} | {error, not_found}.
276 muc_light_room_name_to_jid_and_aff(UserJID, RoomName, Domain) ->
277 5 UserUS = jid:to_lus(UserJID),
278 5 case get_user_rooms(UserUS, Domain) of
279 [] ->
280 1 {error, given_user_does_not_occupy_any_room};
281 RoomUSs when is_list(RoomUSs) ->
282 4 FindFun = find_room_and_user_aff_by_room_name(RoomName, UserUS),
283 4 case lists:foldl(FindFun, none, RoomUSs) of
284 {ok, {RU, RS}, UserAff} ->
285 3 true = is_subdomain(RS, Domain),
286 3 {ok, jid:make(RU, RS, <<>>), UserAff};
287 none ->
288 1 {error, not_found}
289 end
290 end.
291
292 -spec get_user_rooms(UserUS :: jid:simple_bare_jid(), Domain :: jid:lserver()) ->
293 [jid:simple_bare_jid()].
294 get_user_rooms({_, UserS} = UserUS, Domain) ->
295 6 HostType = mod_muc_light_utils:server_host_to_host_type(UserS),
296 6 mod_muc_light_db_backend:get_user_rooms(HostType, UserUS, Domain).
297
298 -spec get_room_name_and_user_aff(RoomUS :: jid:simple_bare_jid(),
299 UserUS :: jid:simple_bare_jid()) ->
300 {ok, RoomName :: binary(), UserAff :: aff()} | {error, not_exists}.
301 get_room_name_and_user_aff(RoomUS, {_, UserS} = UserUS) ->
302 5 HostType = mod_muc_light_utils:server_host_to_host_type(UserS),
303 5 case mod_muc_light_db_backend:get_info(HostType, RoomUS) of
304 {ok, Cfg, Affs, _} ->
305 5 {roomname, RoomName} = lists:keyfind(roomname, 1, Cfg),
306 5 {_, UserAff} = lists:keyfind(UserUS, 1, Affs),
307 5 {ok, RoomName, UserAff};
308 Error ->
309
:-(
Error
310 end.
311
312 -type find_room_acc() :: {ok, RoomUS :: jid:simple_bare_jid(), UserAff :: aff()} | none.
313
314 -spec find_room_and_user_aff_by_room_name(RoomName :: binary(),
315 UserUS :: jid:simple_bare_jid()) ->
316 fun((RoomUS :: jid:simple_bare_jid(), find_room_acc()) -> find_room_acc()).
317 find_room_and_user_aff_by_room_name(RoomName, UserUS) ->
318 5 fun (RoomUS, none) ->
319 5 case get_room_name_and_user_aff(RoomUS, UserUS) of
320 {ok, RoomName, UserAff} ->
321 4 {ok, RoomUS, UserAff};
322 _ ->
323 1 none
324 end;
325 (_, Acc) when Acc =/= none ->
326
:-(
Acc
327 end.
328
329 is_subdomain(Child, Parent) ->
330 %% Example input Child = <<"muclight.localhost">> and Parent =
331 %% <<"localhost">>
332 4 case binary:match(Child, Parent) of
333
:-(
nomatch -> false;
334 4 {_, _} -> true
335 end.
336
337 iq(To, From, Type, Children) ->
338 16 UUID = uuid:uuid_to_string(uuid:get_v4(), binary_standard),
339 16 #xmlel{name = <<"iq">>,
340 attrs = [{<<"from">>, From},
341 {<<"to">>, To},
342 {<<"type">>, Type},
343 {<<"id">>, UUID}],
344 children = Children
345 }.
346
347 query(NS, Children) when is_binary(NS), is_list(Children) ->
348 16 #xmlel{name = <<"query">>,
349 attrs = [{<<"xmlns">>, NS}],
350 children = Children
351 }.
352
353 affiliate(JID, Kind) when is_binary(JID), is_binary(Kind) ->
354 16 #xmlel{name = <<"user">>,
355 attrs = [{<<"affiliation">>, Kind}],
356 children = [ #xmlcdata{ content = JID } ]
357 }.
358
Line Hits Source