./ct_report/coverage/mod_muc_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 Mult-user Chat (MUC)
18 %%==============================================================================
19
20 -module(mod_muc_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_instant_room/4]).
28 -export([invite_to_room/5]).
29 -export([send_message_to_room/4]).
30 -export([kick_user_from_room/3]).
31
32 -ignore_xref([create_instant_room/4, invite_to_room/5, kick_user_from_room/3,
33 send_message_to_room/4]).
34
35 -include("mongoose.hrl").
36 -include("jlib.hrl").
37 -include("mod_muc_room.hrl").
38
39 start(_, _) ->
40 308 mongoose_commands:register(commands()).
41
42 stop(_) ->
43 308 mongoose_commands:unregister(commands()).
44
45 -spec supported_features() -> [atom()].
46 supported_features() ->
47 142 [dynamic_domains].
48
49 commands() ->
50 616 [
51 [{name, create_muc_room},
52 {category, <<"mucs">>},
53 {desc, <<"Create a MUC room.">>},
54 {module, ?MODULE},
55 {function, create_instant_room},
56 {action, create},
57 {identifiers, [domain]},
58 {args,
59 %% The argument `domain' is what we normally term the XMPP
60 %% domain, `name' is the room name, `owner' is the XMPP entity
61 %% that would normally request an instant MUC room.
62 [{domain, binary},
63 {name, binary},
64 {owner, binary},
65 {nick, binary}]},
66 {result, {name, binary}}],
67
68 [{name, invite_to_muc_room},
69 {category, <<"mucs">>},
70 {subcategory, <<"participants">>},
71 {desc, <<"Send a MUC room invite from one user to another.">>},
72 {module, ?MODULE},
73 {function, invite_to_room},
74 {action, create},
75 {identifiers, [domain, name]},
76 {args,
77 [{domain, binary},
78 {name, binary},
79 {sender, binary},
80 {recipient, binary},
81 {reason, binary}
82 ]},
83 {result, ok}],
84
85 [{name, send_message_to_room},
86 {category, <<"mucs">>},
87 {subcategory, <<"messages">>},
88 {desc, <<"Send a message to a MUC room from a given user.">>},
89 {module, ?MODULE},
90 {function, send_message_to_room},
91 {action, create},
92 {identifiers, [domain, name]},
93 {args,
94 [{domain, binary},
95 {name, binary},
96 {from, binary},
97 {body, binary}
98 ]},
99 {result, ok}],
100
101 [{name, kick_user_from_room},
102 {category, <<"mucs">>},
103 {desc,
104 <<"Kick a user from a MUC room (on behalf of a moderator).">>},
105 {module, ?MODULE},
106 {function, kick_user_from_room},
107 {action, delete},
108 {identifiers, [domain, name, nick]},
109 {args,
110 [{domain, binary},
111 {name, binary},
112 {nick, binary}
113 ]},
114 {result, ok}]
115
116 ].
117
118 create_instant_room(Domain, Name, Owner, Nick) ->
119 %% Because these stanzas are sent on the owner's behalf through
120 %% the HTTP API, they will certainly recieve stanzas as a
121 %% consequence, even if their client(s) did not initiate this.
122 5 OwnerJID = jid:binary_to_bare(Owner),
123 5 BareRoomJID = #jid{server = MUCDomain} = room_jid(Domain, Name),
124 5 UserRoomJID = jid:make(Name, MUCDomain, Nick),
125 %% Send presence to create a room.
126 5 ejabberd_router:route(OwnerJID, UserRoomJID,
127 presence(OwnerJID, UserRoomJID)),
128 %% Send IQ set to unlock the room.
129 5 ejabberd_router:route(OwnerJID, BareRoomJID,
130 declination(OwnerJID, BareRoomJID)),
131 5 case verify_room(BareRoomJID, OwnerJID) of
132 ok ->
133 5 Name;
134 Error ->
135
:-(
Error
136 end.
137
138 invite_to_room(Domain, Name, Sender, Recipient, Reason) ->
139 10 case mongoose_stanza_helper:parse_from_to(Sender, Recipient) of
140 {ok, SenderJid, RecipientJid} ->
141 6 RoomJid = room_jid(Domain, Name),
142 6 case verify_room(RoomJid, SenderJid) of
143 ok ->
144 %% Direct invitation: i.e. not mediated by MUC room. See XEP 0249.
145 3 X = #xmlel{name = <<"x">>,
146 attrs = [{<<"xmlns">>, ?NS_CONFERENCE},
147 {<<"jid">>, jid:to_binary(RoomJid)},
148 {<<"reason">>, Reason}]
149 },
150 3 Invite = message(SenderJid, RecipientJid, <<>>, [ X ]),
151 3 ejabberd_router:route(SenderJid, RecipientJid, Invite);
152 Error ->
153 3 Error
154 end;
155 Error ->
156 4 Error
157 end.
158
159 send_message_to_room(Domain, Name, Sender, Message) ->
160 2 RoomJid = room_jid(Domain, Name),
161 2 case jid:from_binary(Sender) of
162 error ->
163
:-(
error;
164 SenderJid ->
165 2 Body = #xmlel{name = <<"body">>,
166 children = [ #xmlcdata{ content = Message } ]
167 },
168 2 Stanza = message(SenderJid, RoomJid, <<"groupchat">>, [ Body ]),
169 2 ejabberd_router:route(SenderJid, RoomJid, Stanza)
170 end.
171
172 kick_user_from_room(Domain, Name, Nick) ->
173 %% All the machinery which is already deeply embedded in the MUC
174 %% modules will perform the neccessary checking.
175 1 RoomJid = room_jid(Domain, Name),
176 1 SenderJid = room_moderator(RoomJid),
177 1 Reason = #xmlel{name = <<"reason">>,
178 children = [ #xmlcdata{ content = reason() } ]
179 },
180 1 Item = #xmlel{name = <<"item">>,
181 attrs = [{<<"nick">>, Nick},
182 {<<"role">>, <<"none">>}],
183 children = [ Reason ]
184 },
185 1 IQ = iq(<<"set">>, SenderJid, RoomJid, [ query(?NS_MUC_ADMIN, [ Item ]) ]),
186 1 ejabberd_router:route(SenderJid, RoomJid, IQ).
187
188 %%--------------------------------------------------------------------
189 %% Ancillary
190 %%--------------------------------------------------------------------
191
192 -spec room_jid(jid:lserver(), binary()) -> jid:jid() | error.
193 room_jid(Domain, Name) ->
194 14 {ok, HostType} = mongoose_domain_api:get_domain_host_type(Domain),
195 14 MUCDomain = mod_muc:server_host_to_muc_host(HostType, Domain),
196 14 jid:make(Name, MUCDomain, <<>>).
197
198 -spec verify_room(jid:jid(), jid:jid()) ->
199 ok | {error, internal | not_found, term()}.
200 verify_room(BareRoomJID, OwnerJID) ->
201 11 case mod_muc_room:can_access_room(BareRoomJID, OwnerJID) of
202 {ok, true} ->
203 8 ok;
204 {ok, false} ->
205
:-(
{error, internal, "room is locked"};
206 {error, not_found} ->
207 3 {error, not_found, "room does not exist"}
208 end.
209
210 iq(Type, Sender, Recipient, Children)
211 when is_binary(Type), is_list(Children) ->
212 6 Addresses = address_attributes(Sender, Recipient),
213 6 #xmlel{name = <<"iq">>,
214 attrs = Addresses ++ [{<<"type">>, Type}],
215 children = Children
216 }.
217
218 message(Sender, Recipient, Type, Contents)
219 when is_binary(Type), is_list(Contents) ->
220 5 Addresses = address_attributes(Sender, Recipient),
221 5 Attributes = case Type of
222 3 <<>> -> Addresses;
223 2 _ -> [{<<"type">>, Type}|Addresses]
224 end,
225 5 #xmlel{name = <<"message">>,
226 attrs = Attributes,
227 children = Contents
228 }.
229
230 query(XMLNameSpace, Children)
231 when is_binary(XMLNameSpace), is_list(Children) ->
232 6 #xmlel{name = <<"query">>,
233 attrs = [{<<"xmlns">>, XMLNameSpace}],
234 children = Children
235 }.
236
237 presence(Sender, Recipient) ->
238 5 #xmlel{name = <<"presence">>,
239 attrs = address_attributes(Sender, Recipient),
240 children = [#xmlel{name = <<"x">>,
241 attrs = [{<<"xmlns">>, ?NS_MUC}]}]
242 }.
243
244 declination(Sender, Recipient) ->
245 5 iq(<<"set">>, Sender, Recipient, [ data_submission() ]).
246
247 data_submission() ->
248 5 query(?NS_MUC_OWNER, [#xmlel{name = <<"x">>,
249 attrs = [{<<"xmlns">>, ?NS_XDATA},
250 {<<"type">>, <<"submit">>}]}]).
251
252 address_attributes(Sender, Recipient) ->
253 16 [
254 {<<"from">>, jid:to_binary(Sender)},
255 {<<"to">>, jid:to_binary(Recipient)}
256 ].
257
258 reason() ->
259 1 <<"Kicked through HTTP Administration API.">>.
260
261
262 room_moderator(RoomJID) ->
263 1 [JIDStruct|_] =
264 1 [ UserJID
265 || #user{ jid = UserJID,
266 1 role = moderator } <- room_users(RoomJID) ],
267 1 JIDStruct.
268
269 room_users(RoomJID) ->
270 1 {ok, Affiliations} = mod_muc_room:get_room_users(RoomJID),
271 1 Affiliations.
Line Hits Source