./ct_report/coverage/mod_muc_light_room.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : mod_muc_light_room.erl
3 %%% Author : Piotr Nosek <piotr.nosek@erlang-solutions.com>
4 %%% Purpose : Room logic for mod_muc_light
5 %%% Created : 9 Sep 2014 by Piotr Nosek <piotr.nosek@erlang-solutions.com>
6 %%%
7 %%% This program is free software; you can redistribute it and/or
8 %%% modify it under the terms of the GNU General Public License as
9 %%% published by the Free Software Foundation; either version 2 of the
10 %%% License, or (at your option) any later version.
11 %%%
12 %%% This program is distributed in the hope that it will be useful,
13 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
14 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 %%% General Public License for more details.
16 %%%
17 %%% You should have received a copy of the GNU General Public License
18 %%% along with this program; if not, write to the Free Software
19 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 %%%
21 %%%----------------------------------------------------------------------
22
23 -module(mod_muc_light_room).
24 -author('piotr.nosek@erlang-solutions.com').
25
26 %% API
27 -export([handle_request/5, maybe_forget/4, process_request/5]).
28
29 %% Callbacks
30 -export([participant_limit_check/2]).
31
32 -include("mongoose.hrl").
33 -include("jlib.hrl").
34 -include("mod_muc_light.hrl").
35
36 -type packet_processing_result() :: muc_light_encode_request() | {error, Reason :: term()}.
37
38 %%====================================================================
39 %% API
40 %%====================================================================
41
42 -spec handle_request(From :: jid:jid(), RoomJID :: jid:jid(), OrigPacket :: exml:element(),
43 Request :: muc_light_packet(), Acc :: mongoose_acc:t()) -> mongoose_acc:t().
44 handle_request(From, Room, OrigPacket, Request, Acc1) ->
45 308 Acc2 = mongoose_hooks:acc_room_affiliations(Acc1, Room),
46 308 AffUsersRes = mod_muc_light:get_room_affiliations_from_acc(Acc2),
47 308 Response = process_request(From, Room, Request, AffUsersRes, Acc2),
48 308 send_response(From, Room, OrigPacket, Response, Acc2).
49
50 -spec maybe_forget(Acc :: mongoose_acc:t(),
51 RoomUS :: jid:simple_bare_jid(),
52 NewAffUsers :: aff_users(),
53 Version :: binary() ) -> any().
54 maybe_forget(Acc, {RoomU, RoomS} = RoomUS, [], _Version) ->
55 76 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
56 76 mongoose_hooks:forget_room(HostType, RoomS, RoomU),
57 76 mod_muc_light_db_backend:destroy_room(HostType, RoomUS);
58 maybe_forget(Acc, {RoomU, RoomS}, NewAffs, Version) ->
59 95 RoomJid = jid:make_noprep(RoomU, RoomS, <<>>),
60 95 mongoose_hooks:room_new_affiliations(Acc, RoomJid, NewAffs, Version),
61 95 my_room_will_go_on.
62
63 %%====================================================================
64 %% Callbacks
65 %%====================================================================
66
67 -spec participant_limit_check(RoomUS :: jid:simple_bare_jid(),
68 NewAffUsers :: aff_users()) ->
69 ok | {error, occupant_limit_exceeded}.
70 participant_limit_check({_, MUCServer} = _RoomUS, NewAffUsers) ->
71 69 HostType = mod_muc_light_utils:muc_host_to_host_type(MUCServer),
72 69 MaxOccupants = gen_mod:get_module_opt(
73 HostType, mod_muc_light, max_occupants, ?DEFAULT_MAX_OCCUPANTS),
74 69 case length(NewAffUsers) > MaxOccupants of
75 1 true -> {error, occupant_limit_exceeded};
76 68 false -> ok
77 end.
78
79 %%====================================================================
80 %% Packet handling
81 %%====================================================================
82
83 -spec process_request(From :: jid:jid(),
84 RoomBareJid :: jid:jid(),
85 Request :: muc_light_packet(),
86 AffUsersRes :: {ok, aff_users(), binary()} | {error, term()},
87 Acc :: mongoose_acc:t()) ->
88 packet_processing_result().
89 process_request(_From, _RoomBareJid, _Request, {error, _} = Error, _Acc) ->
90 2 Error;
91 process_request(From, RoomBareJid, Request, {ok, AffUsers, _Ver}, Acc) ->
92 317 UserUS = jid:to_lus(From),
93 317 RoomUS = jid:to_lus(RoomBareJid),
94 317 Auth = lists:keyfind(UserUS, 1, AffUsers),
95 317 process_request(Request, From, RoomUS, Auth, AffUsers, Acc).
96
97 -spec process_request(Request :: muc_light_packet(),
98 From :: jid:jid(),
99 RoomUS :: jid:simple_bare_jid(),
100 Auth :: false | aff_user(),
101 AffUsers :: aff_users(),
102 Acc :: mongoose_acc:t()) ->
103 packet_processing_result().
104 process_request(_Request, _From, _RoomUS, false, _AffUsers, _Acc) ->
105 4 {error, item_not_found};
106 process_request(#msg{} = Msg, _From, _RoomUS, _Auth, AffUsers, _Acc) ->
107 167 {Msg, AffUsers};
108 process_request({get, #config{} = ConfigReq},
109 _From, RoomUS, _Auth, _AffUsers, Acc) ->
110 20 {_, RoomS} = RoomUS,
111 20 HostType = mongoose_acc:host_type(Acc),
112 20 {ok, Config, RoomVersion} = mod_muc_light_db_backend:get_config(HostType, RoomUS),
113 20 RawConfig = mod_muc_light_room_config:to_binary_kv(Config, mod_muc_light:config_schema(RoomS)),
114 20 {get, ConfigReq#config{ version = RoomVersion,
115 raw_config = RawConfig }};
116 process_request({get, #affiliations{} = AffReq},
117 _From, RoomUS, _Auth, _AffUsers, Acc) ->
118 9 HostType = mongoose_acc:host_type(Acc),
119 9 {ok, AffUsers, RoomVersion} = mod_muc_light_db_backend:get_aff_users(HostType, RoomUS),
120 9 {get, AffReq#affiliations{ version = RoomVersion,
121 aff_users = AffUsers }};
122 process_request({get, #info{} = InfoReq},
123 _From, {_, RoomS} = RoomUS, _Auth, _AffUsers, Acc) ->
124 3 HostType = mongoose_acc:host_type(Acc),
125 3 {ok, Config, AffUsers, RoomVersion} = mod_muc_light_db_backend:get_info(HostType, RoomUS),
126 3 RawConfig = mod_muc_light_room_config:to_binary_kv(Config, mod_muc_light:config_schema(RoomS)),
127 3 {get, InfoReq#info{ version = RoomVersion, aff_users = AffUsers,
128 raw_config = RawConfig }};
129 process_request({set, #config{} = ConfigReq},
130 _From, RoomUS, {_, UserAff}, AffUsers, Acc) ->
131 28 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
132 28 AllCanConfigure = all_can_configure(HostType),
133 28 process_config_set(HostType, ConfigReq, RoomUS, UserAff, AffUsers, AllCanConfigure);
134 process_request({set, #affiliations{} = AffReq},
135 From, RoomUS, {_, UserAff}, AffUsers, Acc) ->
136 81 UserUS = jid:to_lus(From),
137 81 HostType = mod_muc_light_utils:acc_to_host_type(Acc),
138 81 OwnerUS = case lists:keyfind(owner, 2, AffUsers) of
139
:-(
false -> undefined;
140 81 {OwnerUS0, _} -> OwnerUS0
141 end,
142 81 ValidateResult
143 = case UserAff of
144 owner ->
145 58 {ok, mod_muc_light_utils:filter_out_prevented(HostType,
146 UserUS, RoomUS, AffReq#affiliations.aff_users)};
147 member ->
148 23 AllCanInvite = all_can_invite(HostType),
149 23 validate_aff_changes_by_member(
150 AffReq#affiliations.aff_users, [], UserUS, OwnerUS, RoomUS, AllCanInvite)
151 end,
152 81 process_aff_set(AffReq, RoomUS, ValidateResult, Acc);
153 process_request({set, #destroy{} = DestroyReq},
154 _From, RoomUS, {_, owner}, AffUsers, Acc) ->
155 5 HostType = mongoose_acc:host_type(Acc),
156 5 ok = mod_muc_light_db_backend:destroy_room(HostType, RoomUS),
157 5 maybe_forget(Acc, RoomUS, [], <<>>),
158 5 {set, DestroyReq, AffUsers};
159 process_request({set, #destroy{}},
160 _From, _RoomUS, _Auth, _AffUsers, _Acc) ->
161
:-(
{error, not_allowed};
162 process_request(_UnknownReq, _From, _RoomUS, _Auth, _AffUsers, _Acc) ->
163
:-(
{error, bad_request}.
164
165 all_can_invite(HostType) ->
166 23 gen_mod:get_module_opt(HostType, mod_muc_light, all_can_invite, ?DEFAULT_ALL_CAN_INVITE).
167
168 all_can_configure(HostType) ->
169 28 gen_mod:get_module_opt(HostType, mod_muc_light, all_can_configure, ?DEFAULT_ALL_CAN_CONFIGURE).
170
171 %% --------- Config set ---------
172
173 -spec process_config_set(HostType :: mongooseim:host_type(),
174 ConfigReq :: config_req_props(),
175 RoomUS :: jid:simple_bare_jid(),
176 UserAff :: member | owner, AffUsers :: aff_users(),
177 UserAllowedToConfigure :: boolean()) ->
178 {set, config_req_props(), aff_users()} | {error, not_allowed} | validation_error().
179 process_config_set(HostType, #config{ raw_config = [{<<"subject">>, _}] } = ConfigReq, RoomUS, UserAff,
180 AffUsers, false) ->
181 % Everyone is allowed to change subject
182 6 process_config_set(HostType, ConfigReq, RoomUS, UserAff, AffUsers, true);
183 process_config_set(_HostType, _ConfigReq, _RoomUS, member, _AffUsers, false) ->
184 5 {error, not_allowed};
185 process_config_set(HostType, ConfigReq, {_, RoomS} = RoomUS, _UserAff, AffUsers, _AllCanConfigure) ->
186 23 case mod_muc_light_room_config:from_binary_kv_diff(
187 ConfigReq#config.raw_config, mod_muc_light:config_schema(RoomS)) of
188 {ok, Config} ->
189 22 NewVersion = mongoose_bin:gen_from_timestamp(),
190 22 {ok, PrevVersion} = mod_muc_light_db_backend:set_config(HostType, RoomUS, Config, NewVersion),
191 22 {set, ConfigReq#config{ prev_version = PrevVersion, version = NewVersion }, AffUsers};
192 Error ->
193 1 Error
194 end.
195
196 %% --------- Affiliation set ---------
197
198 %% Member can only add new members or leave
199 -spec validate_aff_changes_by_member(AffUsersChanges :: aff_users(),
200 AffUsersChangesAcc :: aff_users(),
201 UserUS :: jid:simple_bare_jid(),
202 OwnerUS :: jid:simple_bare_jid(),
203 RoomUS :: jid:simple_bare_jid(),
204 AllCanInvite :: boolean()) ->
205 {ok, aff_users()} | {error, not_allowed}.
206 validate_aff_changes_by_member([], Acc, _UserUS, _OwnerUS, _RoomUS, _AllCanInvite) ->
207 16 {ok, Acc};
208 validate_aff_changes_by_member([{UserUS, none} | RAffUsersChanges], Acc, UserUS, OwnerUS,
209 RoomUS, AllCanInvite) ->
210 14 validate_aff_changes_by_member(RAffUsersChanges, [{UserUS, none} | Acc], UserUS, OwnerUS,
211 RoomUS, AllCanInvite);
212 validate_aff_changes_by_member([{OwnerUS, _} | _RAffUsersChanges], _Acc, _UserUS, OwnerUS,
213 _RoomUS, _AllCanInvite) ->
214 1 {error, not_allowed};
215 validate_aff_changes_by_member([{_, member} = AffUserChange | RAffUsersChanges], Acc, UserUS,
216 OwnerUS, RoomUS, true) ->
217 2 validate_aff_changes_by_member(
218 RAffUsersChanges, [AffUserChange | Acc], UserUS, OwnerUS, RoomUS, true);
219 validate_aff_changes_by_member(_AffUsersChanges, _Acc, _UserUS, _OwnerUS, _RoomUS, _AllCanInvite) ->
220 6 {error, not_allowed}.
221
222 -spec process_aff_set(AffReq :: affiliations_req_props(),
223 RoomUS :: jid:simple_bare_jid(),
224 ValidateResult :: {ok, aff_users()} | {error, not_allowed},
225 Acc :: mongoose_acc:t()) ->
226 {set, affiliations_req_props(), OldAffUsers :: aff_users(), NewAffUsers :: aff_users()}
227 | {error, not_allowed}.
228 process_aff_set(AffReq, _RoomUS, {ok, []}, _Acc) -> % It seems that all users blocked this request
229 4 {set, AffReq, [], []}; % Just return result to the user, don't change or broadcast anything
230 process_aff_set(AffReq, RoomUS, {ok, FilteredAffUsers}, Acc) ->
231 70 NewVersion = mongoose_bin:gen_from_timestamp(),
232 70 HostType = mongoose_acc:host_type(Acc),
233 70 case mod_muc_light_db_backend:modify_aff_users(HostType, RoomUS, FilteredAffUsers,
234 fun ?MODULE:participant_limit_check/2, NewVersion) of
235 {ok, OldAffUsers, NewAffUsers, AffUsersChanged, OldVersion} ->
236 68 maybe_forget(Acc, RoomUS, NewAffUsers, NewVersion),
237 68 {set, AffReq#affiliations{
238 prev_version = OldVersion,
239 version = NewVersion,
240 aff_users = AffUsersChanged
241 }, OldAffUsers, NewAffUsers};
242 Error ->
243 2 Error
244 end;
245 process_aff_set(_AffReq, _RoomUS, Error, _Acc) ->
246 7 Error.
247
248 %%====================================================================
249 %% Response processing
250 %%====================================================================
251
252 -spec send_response(From :: jid:jid(), RoomJID :: jid:jid(), OrigPacket :: exml:element(),
253 Result :: packet_processing_result(), Acc :: mongoose_acc:t()) -> mongoose_acc:t().
254 send_response(From, RoomJID, OrigPacket, {error, _} = Err, Acc) ->
255 14 mod_muc_light_codec_backend:encode_error(
256 Err, From, RoomJID, OrigPacket, Acc);
257 send_response(From, RoomJID, _OriginalPacket, Response, Acc) ->
258 294 F = make_handler_fun(Acc),
259 294 mod_muc_light_codec_backend:encode(Response, From, RoomJID, F, Acc).
260
261 %%====================================================================
262 %% Internal functions
263 %%====================================================================
264 make_handler_fun(Acc) ->
265 294 fun(From, To, Packet) ->
266 743 NewAcc0 = mongoose_acc:new(#{location => ?LOCATION,
267 lserver => From#jid.lserver,
268 element => Packet,
269 from_jid => From,
270 to_jid => To}),
271 743 PermanentFields = mongoose_acc:get_permanent_fields(Acc),
272 743 NewAcc = mongoose_acc:set_permanent(PermanentFields, NewAcc0),
273 743 ejabberd_router:route(From, To, NewAcc, Packet)
274 end.
Line Hits Source