2003-03-23 21:08:44 +01:00
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
%%% File : mod_muc_room.erl
|
2007-12-24 14:57:53 +01:00
|
|
|
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
2003-03-23 21:08:44 +01:00
|
|
|
%%% Purpose : MUC room stuff
|
2007-12-24 14:57:53 +01:00
|
|
|
%%% Created : 19 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
|
|
|
|
%%%
|
|
|
|
%%%
|
2010-01-12 17:15:16 +01:00
|
|
|
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
2007-12-24 14:57:53 +01:00
|
|
|
%%%
|
|
|
|
%%% This program is free software; you can redistribute it and/or
|
|
|
|
%%% modify it under the terms of the GNU General Public License as
|
|
|
|
%%% published by the Free Software Foundation; either version 2 of the
|
|
|
|
%%% License, or (at your option) any later version.
|
|
|
|
%%%
|
|
|
|
%%% This program is distributed in the hope that it will be useful,
|
|
|
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
%%% General Public License for more details.
|
2009-01-19 15:47:33 +01:00
|
|
|
%%%
|
2007-12-24 14:57:53 +01:00
|
|
|
%%% You should have received a copy of the GNU General Public License
|
|
|
|
%%% along with this program; if not, write to the Free Software
|
|
|
|
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
|
|
%%% 02111-1307 USA
|
|
|
|
%%%
|
2003-03-23 21:08:44 +01:00
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
|
|
|
|
-module(mod_muc_room).
|
2007-12-24 14:57:53 +01:00
|
|
|
-author('alexey@process-one.net').
|
2003-03-23 21:08:44 +01:00
|
|
|
|
|
|
|
-behaviour(gen_fsm).
|
|
|
|
|
|
|
|
|
|
|
|
%% External exports
|
2007-09-01 23:05:04 +02:00
|
|
|
-export([start_link/9,
|
|
|
|
start_link/7,
|
|
|
|
start/9,
|
|
|
|
start/7,
|
2003-03-23 21:08:44 +01:00
|
|
|
route/4]).
|
|
|
|
|
|
|
|
%% gen_fsm callbacks
|
|
|
|
-export([init/1,
|
|
|
|
normal_state/2,
|
|
|
|
handle_event/3,
|
|
|
|
handle_sync_event/4,
|
|
|
|
handle_info/3,
|
|
|
|
terminate/3,
|
|
|
|
code_change/4]).
|
|
|
|
|
2008-12-01 16:01:27 +01:00
|
|
|
-include_lib("exmpp/include/exmpp.hrl").
|
|
|
|
|
2003-03-23 21:08:44 +01:00
|
|
|
-include("ejabberd.hrl").
|
2008-12-08 13:02:27 +01:00
|
|
|
-include("mod_muc_room.hrl").
|
2009-05-26 19:20:09 +02:00
|
|
|
-include("jlib.hrl"). %% Used for captcha
|
2003-03-23 21:08:44 +01:00
|
|
|
|
2007-08-29 19:54:45 +02:00
|
|
|
-define(MAX_USERS_DEFAULT_LIST,
|
|
|
|
[5, 10, 20, 30, 50, 100, 200, 500, 1000, 2000, 5000]).
|
2007-08-28 16:35:50 +02:00
|
|
|
|
2003-09-19 20:22:44 +02:00
|
|
|
%-define(DBGFSM, true).
|
2003-03-23 21:08:44 +01:00
|
|
|
|
|
|
|
-ifdef(DBGFSM).
|
|
|
|
-define(FSMOPTS, [{debug, [trace]}]).
|
|
|
|
-else.
|
|
|
|
-define(FSMOPTS, []).
|
|
|
|
-endif.
|
|
|
|
|
2007-09-14 16:16:36 +02:00
|
|
|
%% Module start with or without supervisor:
|
|
|
|
-ifdef(NO_TRANSIENT_SUPERVISORS).
|
|
|
|
-define(SUPERVISOR_START,
|
|
|
|
gen_fsm:start(?MODULE, [Host, ServerHost, Access, Room, HistorySize,
|
|
|
|
RoomShaper, Creator, Nick, DefRoomOpts],
|
|
|
|
?FSMOPTS)).
|
|
|
|
-else.
|
|
|
|
-define(SUPERVISOR_START,
|
|
|
|
Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup),
|
|
|
|
supervisor:start_child(
|
|
|
|
Supervisor, [Host, ServerHost, Access, Room, HistorySize, RoomShaper,
|
|
|
|
Creator, Nick, DefRoomOpts])).
|
|
|
|
-endif.
|
2003-03-23 21:08:44 +01:00
|
|
|
|
2008-12-01 16:01:27 +01:00
|
|
|
|
|
|
|
-define(ERR(Packet,Type, Lang, ErrText),
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns,
|
|
|
|
Type,
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})).
|
|
|
|
|
2003-03-23 21:08:44 +01:00
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
%%% API
|
|
|
|
%%%----------------------------------------------------------------------
|
2007-09-01 23:05:04 +02:00
|
|
|
start(Host, ServerHost, Access, Room, HistorySize, RoomShaper,
|
|
|
|
Creator, Nick, DefRoomOpts) ->
|
2007-09-14 16:16:36 +02:00
|
|
|
?SUPERVISOR_START.
|
2003-03-23 21:08:44 +01:00
|
|
|
|
2007-09-01 23:05:04 +02:00
|
|
|
start(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts) ->
|
2006-02-02 06:00:27 +01:00
|
|
|
Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup),
|
|
|
|
supervisor:start_child(
|
2007-09-01 23:05:04 +02:00
|
|
|
Supervisor, [Host, ServerHost, Access, Room, HistorySize, RoomShaper,
|
|
|
|
Opts]).
|
2006-02-02 06:00:27 +01:00
|
|
|
|
2007-09-01 23:05:04 +02:00
|
|
|
start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper,
|
|
|
|
Creator, Nick, DefRoomOpts) ->
|
|
|
|
gen_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, HistorySize,
|
|
|
|
RoomShaper, Creator, Nick, DefRoomOpts],
|
2006-02-03 04:28:15 +01:00
|
|
|
?FSMOPTS).
|
2006-02-02 06:00:27 +01:00
|
|
|
|
2007-09-01 23:05:04 +02:00
|
|
|
start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts) ->
|
|
|
|
gen_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, HistorySize,
|
|
|
|
RoomShaper, Opts],
|
2006-02-03 04:28:15 +01:00
|
|
|
?FSMOPTS).
|
2003-03-25 22:03:35 +01:00
|
|
|
|
2003-03-23 21:08:44 +01:00
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
%%% Callback functions from gen_fsm
|
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
|
|
|
|
%%----------------------------------------------------------------------
|
|
|
|
%% Func: init/1
|
|
|
|
%% Returns: {ok, StateName, StateData} |
|
|
|
|
%% {ok, StateName, StateData, Timeout} |
|
|
|
|
%% ignore |
|
2007-08-28 16:35:50 +02:00
|
|
|
%% {stop, StopReason}
|
2003-03-23 21:08:44 +01:00
|
|
|
%%----------------------------------------------------------------------
|
2009-01-15 20:41:06 +01:00
|
|
|
init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick, DefRoomOpts]) ->
|
2008-02-28 01:30:23 +01:00
|
|
|
process_flag(trap_exit, true),
|
2007-09-01 23:05:04 +02:00
|
|
|
Shaper = shaper:new(RoomShaper),
|
2003-03-23 21:08:44 +01:00
|
|
|
State = set_affiliation(Creator, owner,
|
|
|
|
#state{host = Host,
|
2005-06-20 05:18:13 +02:00
|
|
|
server_host = ServerHost,
|
2004-05-17 22:36:41 +02:00
|
|
|
access = Access,
|
2003-10-07 22:31:44 +02:00
|
|
|
room = Room,
|
2006-09-05 17:53:54 +02:00
|
|
|
history = lqueue_new(HistorySize),
|
2009-06-01 18:26:00 +02:00
|
|
|
jid = exmpp_jid:make(Room, Host),
|
2007-09-01 23:05:04 +02:00
|
|
|
just_created = true,
|
|
|
|
room_shaper = Shaper}),
|
2007-08-02 04:30:25 +02:00
|
|
|
State1 = set_opts(DefRoomOpts, State),
|
2007-12-23 13:28:44 +01:00
|
|
|
?INFO_MSG("Created MUC room ~s@~s by ~s",
|
2009-06-01 18:59:08 +02:00
|
|
|
[Room, Host, exmpp_jid:to_binary(Creator)]),
|
2009-12-29 15:44:12 +01:00
|
|
|
add_to_log(room_existence, created, State1),
|
|
|
|
add_to_log(room_existence, started, State1),
|
2007-08-02 04:30:25 +02:00
|
|
|
{ok, normal_state, State1};
|
2009-01-15 20:41:06 +01:00
|
|
|
init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts]) ->
|
2008-02-28 01:30:23 +01:00
|
|
|
process_flag(trap_exit, true),
|
2007-09-01 23:05:04 +02:00
|
|
|
Shaper = shaper:new(RoomShaper),
|
2003-03-25 22:03:35 +01:00
|
|
|
State = set_opts(Opts, #state{host = Host,
|
2005-06-20 05:18:13 +02:00
|
|
|
server_host = ServerHost,
|
2004-05-17 22:36:41 +02:00
|
|
|
access = Access,
|
2003-10-07 22:31:44 +02:00
|
|
|
room = Room,
|
2006-09-05 17:53:54 +02:00
|
|
|
history = lqueue_new(HistorySize),
|
2009-06-01 18:26:00 +02:00
|
|
|
jid = exmpp_jid:make(Room, Host),
|
2007-09-01 23:05:04 +02:00
|
|
|
room_shaper = Shaper}),
|
2009-12-29 15:44:12 +01:00
|
|
|
add_to_log(room_existence, started, State),
|
2003-03-23 21:08:44 +01:00
|
|
|
{ok, normal_state, State}.
|
|
|
|
|
|
|
|
%%----------------------------------------------------------------------
|
|
|
|
%% Func: StateName/2
|
|
|
|
%% Returns: {next_state, NextStateName, NextStateData} |
|
|
|
|
%% {next_state, NextStateName, NextStateData, Timeout} |
|
2007-08-28 16:35:50 +02:00
|
|
|
%% {stop, Reason, NewStateData}
|
2003-03-23 21:08:44 +01:00
|
|
|
%%----------------------------------------------------------------------
|
2008-12-01 16:01:27 +01:00
|
|
|
normal_state({route, From, undefined,
|
|
|
|
#xmlel{name = 'message'} = Packet},
|
2003-03-23 21:08:44 +01:00
|
|
|
StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
Lang = exmpp_stanza:get_lang(Packet),
|
2008-06-13 20:55:26 +02:00
|
|
|
case is_user_online(From, StateData) orelse
|
|
|
|
is_user_allowed_message_nonparticipant(From, StateData) of
|
2003-03-23 21:08:44 +01:00
|
|
|
true ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case exmpp_message:get_type(Packet) of
|
|
|
|
groupchat ->
|
2007-09-01 23:05:04 +02:00
|
|
|
Activity = get_user_activity(From, StateData),
|
2007-06-25 18:43:42 +02:00
|
|
|
Now = now_to_usec(now()),
|
|
|
|
MinMessageInterval =
|
|
|
|
trunc(gen_mod:get_module_opt(
|
|
|
|
StateData#state.server_host,
|
|
|
|
mod_muc, min_message_interval, 0) * 1000000),
|
2009-01-15 20:41:06 +01:00
|
|
|
Size = erlang:iolist_size(exmpp_xml:document_to_iolist(Packet)),
|
2007-09-01 23:05:04 +02:00
|
|
|
{MessageShaper, MessageShaperInterval} =
|
|
|
|
shaper:update(Activity#activity.message_shaper, Size),
|
2003-03-23 21:08:44 +01:00
|
|
|
if
|
2007-09-01 23:05:04 +02:00
|
|
|
Activity#activity.message /= undefined ->
|
|
|
|
ErrText = "Traffic rate limit is exceeded",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:error(Packet#xmlel.ns,
|
|
|
|
'resource-constraint',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)}),
|
2007-09-01 23:05:04 +02:00
|
|
|
ejabberd_router:route(
|
|
|
|
StateData#state.jid,
|
2008-12-01 16:01:27 +01:00
|
|
|
From, exmpp_stanza:reply_with_error(Packet, Err)),
|
2007-09-01 23:05:04 +02:00
|
|
|
{next_state, normal_state, StateData};
|
|
|
|
Now >= Activity#activity.message_time + MinMessageInterval,
|
|
|
|
MessageShaperInterval == 0 ->
|
|
|
|
{RoomShaper, RoomShaperInterval} =
|
|
|
|
shaper:update(StateData#state.room_shaper, Size),
|
|
|
|
RoomQueueEmpty = queue:is_empty(
|
|
|
|
StateData#state.room_queue),
|
|
|
|
if
|
|
|
|
RoomShaperInterval == 0,
|
|
|
|
RoomQueueEmpty ->
|
|
|
|
NewActivity = Activity#activity{
|
|
|
|
message_time = Now,
|
|
|
|
message_shaper = MessageShaper},
|
|
|
|
StateData1 =
|
2008-12-01 16:53:30 +01:00
|
|
|
store_user_activity(
|
|
|
|
From, NewActivity, StateData),
|
|
|
|
StateData2 =
|
|
|
|
StateData1#state{
|
2007-09-01 23:05:04 +02:00
|
|
|
room_shaper = RoomShaper},
|
2008-12-01 16:53:30 +01:00
|
|
|
process_groupchat_message(From, Packet, StateData2);
|
2007-09-01 23:05:04 +02:00
|
|
|
true ->
|
|
|
|
StateData1 =
|
|
|
|
if
|
|
|
|
RoomQueueEmpty ->
|
|
|
|
erlang:send_after(
|
|
|
|
RoomShaperInterval, self(),
|
|
|
|
process_room_queue),
|
|
|
|
StateData#state{
|
|
|
|
room_shaper = RoomShaper};
|
|
|
|
true ->
|
|
|
|
StateData
|
|
|
|
end,
|
|
|
|
NewActivity = Activity#activity{
|
|
|
|
message_time = Now,
|
|
|
|
message_shaper = MessageShaper,
|
|
|
|
message = Packet},
|
|
|
|
RoomQueue = queue:in(
|
|
|
|
{message, From},
|
|
|
|
StateData#state.room_queue),
|
|
|
|
StateData2 =
|
2008-12-01 16:53:30 +01:00
|
|
|
store_user_activity(
|
|
|
|
From, NewActivity, StateData1),
|
|
|
|
StateData3 =
|
|
|
|
StateData2#state{
|
2007-09-01 23:05:04 +02:00
|
|
|
room_queue = RoomQueue},
|
2008-12-01 16:53:30 +01:00
|
|
|
{next_state, normal_state, StateData3}
|
2007-09-01 23:05:04 +02:00
|
|
|
end;
|
|
|
|
true ->
|
|
|
|
MessageInterval =
|
|
|
|
(Activity#activity.message_time +
|
|
|
|
MinMessageInterval - Now) div 1000,
|
|
|
|
Interval = lists:max([MessageInterval,
|
|
|
|
MessageShaperInterval]),
|
|
|
|
erlang:send_after(
|
|
|
|
Interval, self(), {process_user_message, From}),
|
|
|
|
NewActivity = Activity#activity{
|
|
|
|
message = Packet,
|
|
|
|
message_shaper = MessageShaper},
|
2007-06-25 18:43:42 +02:00
|
|
|
StateData1 =
|
2008-12-01 16:53:30 +01:00
|
|
|
store_user_activity(
|
|
|
|
From, NewActivity, StateData),
|
2007-09-01 23:05:04 +02:00
|
|
|
{next_state, normal_state, StateData1}
|
2003-03-23 21:08:44 +01:00
|
|
|
end;
|
2008-12-01 16:01:27 +01:00
|
|
|
error ->
|
2003-05-14 21:58:05 +02:00
|
|
|
case is_user_online(From, StateData) of
|
|
|
|
true ->
|
2008-02-09 11:38:47 +01:00
|
|
|
ErrorText = "This participant is kicked from the room because "
|
|
|
|
"he sent an error message",
|
|
|
|
NewState = expulse_participant(Packet, From, StateData,
|
|
|
|
translate:translate(Lang, ErrorText)),
|
|
|
|
{next_state, normal_state, NewState};
|
2003-05-14 21:58:05 +02:00
|
|
|
_ ->
|
|
|
|
{next_state, normal_state, StateData}
|
|
|
|
end;
|
2008-12-01 16:01:27 +01:00
|
|
|
chat ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ErrText = "It is not allowed to send private messages to the conference",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:error(Packet#xmlel.ns,
|
|
|
|
'not-acceptable',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)}),
|
2004-03-02 22:16:55 +01:00
|
|
|
ejabberd_router:route(
|
|
|
|
StateData#state.jid,
|
2008-12-01 16:01:27 +01:00
|
|
|
From, exmpp_stanza:reply_with_error(Packet, Err)),
|
2004-03-02 22:16:55 +01:00
|
|
|
{next_state, normal_state, StateData};
|
2009-01-03 16:15:38 +01:00
|
|
|
%%TODO: currently exmpp_message:get_type/1 never returns 'undefined'
|
2008-12-17 17:24:15 +01:00
|
|
|
Type when (Type == 'normal') orelse (Type == 'undefined') ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case catch check_invitation(From,
|
|
|
|
exmpp_xml:get_child_elements(Packet),
|
|
|
|
Lang,
|
|
|
|
StateData) of
|
2007-05-03 06:07:29 +02:00
|
|
|
{error, Error} ->
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet, Error),
|
2003-04-13 21:22:46 +02:00
|
|
|
ejabberd_router:route(
|
2003-10-07 22:31:44 +02:00
|
|
|
StateData#state.jid,
|
2003-04-13 21:22:46 +02:00
|
|
|
From, Err),
|
|
|
|
{next_state, normal_state, StateData};
|
|
|
|
IJID ->
|
|
|
|
Config = StateData#state.config,
|
|
|
|
case Config#config.members_only of
|
|
|
|
true ->
|
|
|
|
case get_affiliation(IJID, StateData) of
|
|
|
|
none ->
|
|
|
|
NSD = set_affiliation(
|
|
|
|
IJID,
|
|
|
|
member,
|
|
|
|
StateData),
|
2005-05-09 21:38:16 +02:00
|
|
|
case (NSD#state.config)#config.persistent of
|
|
|
|
true ->
|
|
|
|
mod_muc:store_room(
|
|
|
|
NSD#state.host,
|
|
|
|
NSD#state.room,
|
|
|
|
make_opts(NSD));
|
|
|
|
_ ->
|
|
|
|
ok
|
|
|
|
end,
|
2003-04-13 21:22:46 +02:00
|
|
|
{next_state, normal_state, NSD};
|
|
|
|
_ ->
|
|
|
|
{next_state, normal_state,
|
|
|
|
StateData}
|
|
|
|
end;
|
|
|
|
false ->
|
|
|
|
{next_state, normal_state, StateData}
|
|
|
|
end
|
|
|
|
end;
|
2003-03-23 21:08:44 +01:00
|
|
|
_ ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ErrText = "Improper message type",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:error(Packet#xmlel.ns,
|
|
|
|
'not-acceptable',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)}),
|
2003-03-23 21:08:44 +01:00
|
|
|
ejabberd_router:route(
|
2003-10-07 22:31:44 +02:00
|
|
|
StateData#state.jid,
|
2008-12-01 16:01:27 +01:00
|
|
|
From, exmpp_stanza:reply_with_error(Packet, Err)),
|
2003-03-23 21:08:44 +01:00
|
|
|
{next_state, normal_state, StateData}
|
|
|
|
end;
|
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case exmpp_stanza:is_stanza_error(Packet) of
|
|
|
|
true ->
|
2003-11-10 15:32:19 +01:00
|
|
|
ok;
|
2008-12-01 16:01:27 +01:00
|
|
|
false ->
|
2008-02-06 21:30:58 +01:00
|
|
|
handle_roommessage_from_nonparticipant(Packet, Lang, StateData, From)
|
2003-11-10 15:32:19 +01:00
|
|
|
end,
|
2003-03-23 21:08:44 +01:00
|
|
|
{next_state, normal_state, StateData}
|
|
|
|
end;
|
|
|
|
|
2008-12-01 16:01:27 +01:00
|
|
|
normal_state({route, From, undefined,
|
|
|
|
#xmlel{name = 'iq'} = Packet},
|
2003-03-23 21:08:44 +01:00
|
|
|
StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case exmpp_iq:xmlel_to_iq(Packet) of
|
|
|
|
#iq{type = Type, ns = XMLNS, lang = Lang, payload = SubEl} = IQ when
|
2003-03-27 15:06:17 +01:00
|
|
|
(XMLNS == ?NS_MUC_ADMIN) or
|
|
|
|
(XMLNS == ?NS_MUC_OWNER) or
|
|
|
|
(XMLNS == ?NS_DISCO_INFO) or
|
2009-05-26 19:20:09 +02:00
|
|
|
(XMLNS == ?NS_DISCO_ITEMS) or
|
|
|
|
(XMLNS == ?NS_CAPTCHA) ->
|
2003-03-25 22:03:35 +01:00
|
|
|
Res1 = case XMLNS of
|
|
|
|
?NS_MUC_ADMIN ->
|
2004-02-26 23:00:04 +01:00
|
|
|
process_iq_admin(From, Type, Lang, SubEl, StateData);
|
2003-03-25 22:03:35 +01:00
|
|
|
?NS_MUC_OWNER ->
|
2004-02-26 23:00:04 +01:00
|
|
|
process_iq_owner(From, Type, Lang, SubEl, StateData);
|
2003-03-27 15:06:17 +01:00
|
|
|
?NS_DISCO_INFO ->
|
2004-02-26 23:00:04 +01:00
|
|
|
process_iq_disco_info(From, Type, Lang, StateData);
|
2003-03-27 15:06:17 +01:00
|
|
|
?NS_DISCO_ITEMS ->
|
2009-05-26 19:20:09 +02:00
|
|
|
process_iq_disco_items(From, Type, Lang, StateData);
|
|
|
|
?NS_CAPTCHA ->
|
|
|
|
process_iq_captcha(From, Type, Lang, SubEl, StateData)
|
2003-03-25 22:03:35 +01:00
|
|
|
end,
|
2003-03-23 21:08:44 +01:00
|
|
|
{IQRes, NewStateData} =
|
2003-03-25 22:03:35 +01:00
|
|
|
case Res1 of
|
2008-12-01 16:01:27 +01:00
|
|
|
{result, [], SD} ->
|
|
|
|
{exmpp_iq:result(IQ), SD};
|
2003-03-23 21:08:44 +01:00
|
|
|
{result, Res, SD} ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{exmpp_iq:result(IQ,#xmlel{ns = XMLNS,
|
|
|
|
name = 'query',
|
|
|
|
children = Res}), SD};
|
2003-03-23 21:08:44 +01:00
|
|
|
{error, Error} ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{exmpp_iq:error(IQ, Error), StateData}
|
2003-03-23 21:08:44 +01:00
|
|
|
end,
|
2003-10-07 22:31:44 +02:00
|
|
|
ejabberd_router:route(StateData#state.jid,
|
2003-03-23 21:08:44 +01:00
|
|
|
From,
|
2008-12-01 16:01:27 +01:00
|
|
|
exmpp_iq:iq_to_xmlel(IQRes)),
|
2003-06-30 14:24:35 +02:00
|
|
|
case NewStateData of
|
|
|
|
stop ->
|
|
|
|
{stop, normal, StateData};
|
|
|
|
_ ->
|
|
|
|
{next_state, normal_state, NewStateData}
|
|
|
|
end;
|
2003-11-23 21:11:21 +01:00
|
|
|
reply ->
|
|
|
|
{next_state, normal_state, StateData};
|
2003-03-23 21:08:44 +01:00
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet, 'feature-not-implemented'),
|
2003-10-07 22:31:44 +02:00
|
|
|
ejabberd_router:route(StateData#state.jid, From, Err),
|
2003-03-23 21:08:44 +01:00
|
|
|
{next_state, normal_state, StateData}
|
|
|
|
end;
|
|
|
|
|
|
|
|
normal_state({route, From, Nick,
|
2008-12-01 16:01:27 +01:00
|
|
|
#xmlel{name = 'presence'} = Packet},
|
2003-03-23 21:08:44 +01:00
|
|
|
StateData) ->
|
2007-09-01 23:05:04 +02:00
|
|
|
Activity = get_user_activity(From, StateData),
|
2007-06-25 18:43:42 +02:00
|
|
|
Now = now_to_usec(now()),
|
|
|
|
MinPresenceInterval =
|
|
|
|
trunc(gen_mod:get_module_opt(
|
|
|
|
StateData#state.server_host,
|
|
|
|
mod_muc, min_presence_interval, 0) * 1000000),
|
|
|
|
if
|
|
|
|
(Now >= Activity#activity.presence_time + MinPresenceInterval) and
|
|
|
|
(Activity#activity.presence == undefined) ->
|
|
|
|
NewActivity = Activity#activity{presence_time = Now},
|
2008-12-01 16:53:30 +01:00
|
|
|
StateData1 = store_user_activity(From, NewActivity, StateData),
|
2007-06-25 18:43:42 +02:00
|
|
|
process_presence(From, Nick, Packet, StateData1);
|
2003-06-30 14:24:35 +02:00
|
|
|
true ->
|
2007-06-25 18:43:42 +02:00
|
|
|
if
|
|
|
|
Activity#activity.presence == undefined ->
|
|
|
|
Interval = (Activity#activity.presence_time +
|
|
|
|
MinPresenceInterval - Now) div 1000,
|
|
|
|
erlang:send_after(
|
2007-09-01 23:05:04 +02:00
|
|
|
Interval, self(), {process_user_presence, From});
|
2007-06-25 18:43:42 +02:00
|
|
|
true ->
|
|
|
|
ok
|
|
|
|
end,
|
|
|
|
NewActivity = Activity#activity{presence = {Nick, Packet}},
|
2008-12-01 16:53:30 +01:00
|
|
|
StateData1 = store_user_activity(From, NewActivity, StateData),
|
2003-06-30 14:24:35 +02:00
|
|
|
{next_state, normal_state, StateData1}
|
|
|
|
end;
|
2003-03-23 21:08:44 +01:00
|
|
|
|
|
|
|
normal_state({route, From, ToNick,
|
2008-12-01 16:01:27 +01:00
|
|
|
#xmlel{name = 'message'} = Packet},
|
2003-03-23 21:08:44 +01:00
|
|
|
StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
Type = exmpp_message:get_type(Packet),
|
|
|
|
Lang = exmpp_stanza:get_lang(Packet),
|
2008-01-29 15:49:08 +01:00
|
|
|
case decide_fate_message(Type, Packet, From, StateData) of
|
|
|
|
{expulse_sender, Reason} ->
|
2008-01-29 19:37:45 +01:00
|
|
|
?DEBUG(Reason, []),
|
2008-02-09 11:38:47 +01:00
|
|
|
ErrorText = "This participant is kicked from the room because "
|
|
|
|
"he sent an error message to another participant",
|
|
|
|
NewState = expulse_participant(Packet, From, StateData,
|
|
|
|
translate:translate(Lang, ErrorText)),
|
|
|
|
{next_state, normal_state, NewState};
|
2008-01-29 15:49:08 +01:00
|
|
|
forget_message ->
|
|
|
|
{next_state, normal_state, StateData};
|
|
|
|
continue_delivery ->
|
2008-05-12 22:35:43 +02:00
|
|
|
case {(StateData#state.config)#config.allow_private_messages,
|
|
|
|
is_user_online(From, StateData)} of
|
|
|
|
{true, true} ->
|
2008-01-29 15:49:08 +01:00
|
|
|
case Type of
|
|
|
|
"groupchat" ->
|
|
|
|
ErrText = "It is not allowed to send private "
|
|
|
|
"messages of type \"groupchat\"",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'bad-request',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})),
|
2003-05-14 21:58:05 +02:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(
|
2008-01-09 17:01:16 +01:00
|
|
|
StateData#state.jid,
|
|
|
|
ToNick),
|
|
|
|
From, Err);
|
2008-01-29 15:49:08 +01:00
|
|
|
_ ->
|
|
|
|
case find_jid_by_nick(ToNick, StateData) of
|
|
|
|
false ->
|
|
|
|
ErrText = "Recipient is not in the conference room",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'item-not-found',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})),
|
2008-01-29 15:49:08 +01:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(
|
2008-01-29 15:49:08 +01:00
|
|
|
StateData#state.jid,
|
|
|
|
ToNick),
|
|
|
|
From, Err);
|
|
|
|
ToJID ->
|
|
|
|
{ok, #user{nick = FromNick}} =
|
2008-12-01 16:01:27 +01:00
|
|
|
?DICT:find(jlib:short_prepd_jid(From),
|
2008-01-29 15:49:08 +01:00
|
|
|
StateData#state.users),
|
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(
|
2008-01-29 15:49:08 +01:00
|
|
|
StateData#state.jid,
|
|
|
|
FromNick),
|
|
|
|
ToJID, Packet)
|
|
|
|
end
|
|
|
|
end;
|
2008-05-12 22:35:43 +02:00
|
|
|
{true, false} ->
|
2008-01-29 15:49:08 +01:00
|
|
|
ErrText = "Only occupants are allowed to send messages to the conference",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'not-acceptable',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})),
|
2008-05-12 22:35:43 +02:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(
|
2008-05-12 22:35:43 +02:00
|
|
|
StateData#state.jid,
|
|
|
|
ToNick),
|
|
|
|
From, Err);
|
|
|
|
{false, _} ->
|
|
|
|
ErrText = "It is not allowed to send private messages",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'forbidden',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})),
|
2008-01-29 15:49:08 +01:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(
|
2008-01-29 15:49:08 +01:00
|
|
|
StateData#state.jid,
|
|
|
|
ToNick),
|
|
|
|
From, Err)
|
|
|
|
end,
|
|
|
|
{next_state, normal_state, StateData}
|
|
|
|
end;
|
2003-03-23 21:08:44 +01:00
|
|
|
|
|
|
|
normal_state({route, From, ToNick,
|
2008-12-01 16:01:27 +01:00
|
|
|
#xmlel{name = 'iq'} = Packet},
|
2003-03-23 21:08:44 +01:00
|
|
|
StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
Lang = exmpp_stanza:get_lang(Packet),
|
2009-12-01 21:02:16 +01:00
|
|
|
StanzaId = get_stanza_id(Packet),
|
2004-03-02 22:16:55 +01:00
|
|
|
case {(StateData#state.config)#config.allow_query_users,
|
2009-12-01 21:02:16 +01:00
|
|
|
is_user_online_iq(StanzaId, From, StateData)} of
|
|
|
|
{true, {true, NewId, FromFull}} ->
|
2003-03-25 22:03:35 +01:00
|
|
|
case find_jid_by_nick(ToNick, StateData) of
|
|
|
|
false ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case exmpp_iq:get_type(Packet) of
|
|
|
|
result ->
|
|
|
|
ok;
|
|
|
|
error ->
|
2003-11-23 21:11:21 +01:00
|
|
|
ok;
|
|
|
|
_ ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ErrText = "Recipient is not in the conference room",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'item-not-found',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})),
|
2003-11-23 21:11:21 +01:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(
|
2003-11-23 21:11:21 +01:00
|
|
|
StateData#state.jid, ToNick),
|
|
|
|
From, Err)
|
|
|
|
end;
|
2003-03-25 22:03:35 +01:00
|
|
|
ToJID ->
|
|
|
|
{ok, #user{nick = FromNick}} =
|
2009-12-01 21:02:16 +01:00
|
|
|
?DICT:find(jlib:short_prepd_jid(FromFull),
|
2003-03-25 22:03:35 +01:00
|
|
|
StateData#state.users),
|
2009-12-01 21:02:16 +01:00
|
|
|
{ToJID2, Packet2} = handle_iq_vcard(FromFull, ToJID,
|
|
|
|
StanzaId, NewId,Packet),
|
2003-03-25 22:03:35 +01:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(StateData#state.jid, FromNick),
|
2009-12-01 21:02:16 +01:00
|
|
|
ToJID2, Packet2)
|
2003-03-25 22:03:35 +01:00
|
|
|
end;
|
2009-12-01 21:02:16 +01:00
|
|
|
{_, {false, _, _}} ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case exmpp_iq:get_type(Packet) of
|
|
|
|
result ->
|
|
|
|
ok;
|
|
|
|
error ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ok;
|
|
|
|
_ ->
|
|
|
|
ErrText = "Only occupants are allowed to send queries to the conference",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'not-acceptable',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})),
|
2004-03-02 22:16:55 +01:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(StateData#state.jid, ToNick),
|
2004-03-02 22:16:55 +01:00
|
|
|
From, Err)
|
|
|
|
end;
|
2003-03-25 22:03:35 +01:00
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case exmpp_iq:get_type(Packet) of
|
|
|
|
result ->
|
|
|
|
ok;
|
|
|
|
error ->
|
2003-11-23 21:11:21 +01:00
|
|
|
ok;
|
|
|
|
_ ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ErrText = "Queries to the conference members are not allowed in this room",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'not-allowed',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})),
|
2003-11-23 21:11:21 +01:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(StateData#state.jid, ToNick),
|
2003-11-23 21:11:21 +01:00
|
|
|
From, Err)
|
|
|
|
end
|
2003-03-25 22:03:35 +01:00
|
|
|
end,
|
2003-03-23 21:08:44 +01:00
|
|
|
{next_state, normal_state, StateData};
|
|
|
|
|
2006-01-19 03:17:31 +01:00
|
|
|
normal_state(_Event, StateData) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
{next_state, normal_state, StateData}.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
%%----------------------------------------------------------------------
|
|
|
|
%% Func: handle_event/3
|
|
|
|
%% Returns: {next_state, NextStateName, NextStateData} |
|
|
|
|
%% {next_state, NextStateName, NextStateData, Timeout} |
|
2007-08-28 16:35:50 +02:00
|
|
|
%% {stop, Reason, NewStateData}
|
2003-03-23 21:08:44 +01:00
|
|
|
%%----------------------------------------------------------------------
|
2004-05-17 22:36:41 +02:00
|
|
|
handle_event({service_message, Msg}, _StateName, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
MessagePkt = #xmlel{name = 'message',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('type', <<"groupchat">>)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{name = 'body',
|
|
|
|
children = [#xmlcdata{cdata = Msg}]}]},
|
2003-05-18 18:41:15 +02:00
|
|
|
lists:foreach(
|
2004-05-17 22:36:41 +02:00
|
|
|
fun({_LJID, Info}) ->
|
2003-05-18 18:41:15 +02:00
|
|
|
ejabberd_router:route(
|
2003-10-07 22:31:44 +02:00
|
|
|
StateData#state.jid,
|
2003-05-18 18:41:15 +02:00
|
|
|
Info#user.jid,
|
|
|
|
MessagePkt)
|
|
|
|
end,
|
|
|
|
?DICT:to_list(StateData#state.users)),
|
|
|
|
NSD = add_message_to_history("",
|
2009-06-30 18:55:26 +02:00
|
|
|
StateData#state.jid,
|
2003-05-18 18:41:15 +02:00
|
|
|
MessagePkt,
|
|
|
|
StateData),
|
|
|
|
{next_state, normal_state, NSD};
|
|
|
|
|
2007-02-19 10:45:58 +01:00
|
|
|
handle_event({destroy, Reason}, _StateName, StateData) ->
|
|
|
|
{result, [], stop} =
|
2008-12-01 16:01:27 +01:00
|
|
|
destroy_room(
|
|
|
|
#xmlel{ns = ?NS_MUC_OWNER, name = 'destroy',
|
|
|
|
children = case Reason of
|
|
|
|
none -> [];
|
|
|
|
_Else -> [#xmlel{name = 'reason',
|
|
|
|
children = [#xmlcdata{cdata = Reason}]}]
|
|
|
|
end}, StateData),
|
|
|
|
|
2008-01-16 11:08:17 +01:00
|
|
|
?INFO_MSG("Destroyed MUC room ~s with reason: ~p",
|
2009-06-01 18:59:08 +02:00
|
|
|
[exmpp_jid:to_binary(StateData#state.jid), Reason]),
|
2009-12-29 15:44:12 +01:00
|
|
|
add_to_log(room_existence, destroyed, StateData),
|
2009-12-30 02:29:02 +01:00
|
|
|
{stop, Reason, StateData};
|
2007-02-19 10:45:58 +01:00
|
|
|
handle_event(destroy, StateName, StateData) ->
|
2008-01-16 11:08:17 +01:00
|
|
|
?INFO_MSG("Destroyed MUC room ~s",
|
2009-06-01 18:59:08 +02:00
|
|
|
[exmpp_jid:to_binary(StateData#state.jid)]),
|
2007-02-19 10:45:58 +01:00
|
|
|
handle_event({destroy, none}, StateName, StateData);
|
|
|
|
|
2007-02-19 17:56:06 +01:00
|
|
|
handle_event({set_affiliations, Affiliations}, StateName, StateData) ->
|
|
|
|
{next_state, StateName, StateData#state{affiliations = Affiliations}};
|
|
|
|
|
2004-05-17 22:36:41 +02:00
|
|
|
handle_event(_Event, StateName, StateData) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
{next_state, StateName, StateData}.
|
|
|
|
|
|
|
|
%%----------------------------------------------------------------------
|
|
|
|
%% Func: handle_sync_event/4
|
|
|
|
%% Returns: {next_state, NextStateName, NextStateData} |
|
|
|
|
%% {next_state, NextStateName, NextStateData, Timeout} |
|
|
|
|
%% {reply, Reply, NextStateName, NextStateData} |
|
|
|
|
%% {reply, Reply, NextStateName, NextStateData, Timeout} |
|
|
|
|
%% {stop, Reason, NewStateData} |
|
2007-08-28 16:35:50 +02:00
|
|
|
%% {stop, Reason, Reply, NewStateData}
|
2003-03-23 21:08:44 +01:00
|
|
|
%%----------------------------------------------------------------------
|
2004-05-17 22:36:41 +02:00
|
|
|
handle_sync_event({get_disco_item, JID, Lang}, _From, StateName, StateData) ->
|
2003-05-29 21:07:41 +02:00
|
|
|
FAffiliation = get_affiliation(JID, StateData),
|
|
|
|
FRole = get_role(JID, StateData),
|
|
|
|
Tail =
|
|
|
|
case ((StateData#state.config)#config.public_list == true) orelse
|
|
|
|
(FRole /= none) orelse
|
|
|
|
(FAffiliation == admin) orelse
|
|
|
|
(FAffiliation == owner) of
|
|
|
|
true ->
|
2004-03-08 21:20:15 +01:00
|
|
|
Desc = case (StateData#state.config)#config.public of
|
|
|
|
true ->
|
|
|
|
"";
|
|
|
|
_ ->
|
|
|
|
translate:translate(Lang, "private, ")
|
|
|
|
end,
|
2007-09-01 23:05:04 +02:00
|
|
|
Len = ?DICT:fold(fun(_, _, Acc) -> Acc + 1 end, 0,
|
|
|
|
StateData#state.users),
|
2004-03-08 21:20:15 +01:00
|
|
|
" (" ++ Desc ++ integer_to_list(Len) ++ ")";
|
2003-05-29 21:07:41 +02:00
|
|
|
_ ->
|
2008-02-04 00:17:39 +01:00
|
|
|
" (n/a)"
|
2003-05-29 21:07:41 +02:00
|
|
|
end,
|
2008-12-01 16:01:27 +01:00
|
|
|
Reply = case ((StateData#state.config)#config.public == true) orelse
|
2004-03-08 21:20:15 +01:00
|
|
|
(FRole /= none) orelse
|
|
|
|
(FAffiliation == admin) orelse
|
|
|
|
(FAffiliation == owner) of
|
2003-03-26 21:51:18 +01:00
|
|
|
true ->
|
2009-01-15 20:41:06 +01:00
|
|
|
{item, list_to_binary([get_title(StateData), Tail])};
|
2003-03-26 21:51:18 +01:00
|
|
|
_ ->
|
|
|
|
false
|
|
|
|
end,
|
|
|
|
{reply, Reply, StateName, StateData};
|
2006-11-15 12:41:26 +01:00
|
|
|
handle_sync_event(get_config, _From, StateName, StateData) ->
|
2007-02-19 17:56:06 +01:00
|
|
|
{reply, {ok, StateData#state.config}, StateName, StateData};
|
2006-11-15 12:41:26 +01:00
|
|
|
handle_sync_event(get_state, _From, StateName, StateData) ->
|
2007-02-19 17:56:06 +01:00
|
|
|
{reply, {ok, StateData}, StateName, StateData};
|
2006-11-24 14:24:08 +01:00
|
|
|
handle_sync_event({change_config, Config}, _From, StateName, StateData) ->
|
|
|
|
{result, [], NSD} = change_config(Config, StateData),
|
2007-02-19 17:56:06 +01:00
|
|
|
{reply, {ok, NSD#state.config}, StateName, NSD};
|
2008-05-16 20:05:03 +02:00
|
|
|
handle_sync_event({change_state, NewStateData}, _From, StateName, _StateData) ->
|
|
|
|
{reply, {ok, NewStateData}, StateName, NewStateData};
|
2004-05-17 22:36:41 +02:00
|
|
|
handle_sync_event(_Event, _From, StateName, StateData) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
Reply = ok,
|
|
|
|
{reply, Reply, StateName, StateData}.
|
|
|
|
|
2004-05-17 22:36:41 +02:00
|
|
|
code_change(_OldVsn, StateName, StateData, _Extra) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
{ok, StateName, StateData}.
|
|
|
|
|
|
|
|
%%----------------------------------------------------------------------
|
|
|
|
%% Func: handle_info/3
|
|
|
|
%% Returns: {next_state, NextStateName, NextStateData} |
|
|
|
|
%% {next_state, NextStateName, NextStateData, Timeout} |
|
2007-08-28 16:35:50 +02:00
|
|
|
%% {stop, Reason, NewStateData}
|
2003-03-23 21:08:44 +01:00
|
|
|
%%----------------------------------------------------------------------
|
2007-09-01 23:05:04 +02:00
|
|
|
handle_info({process_user_presence, From}, normal_state = _StateName, StateData) ->
|
2008-04-27 21:06:34 +02:00
|
|
|
RoomQueueEmpty = queue:is_empty(StateData#state.room_queue),
|
|
|
|
RoomQueue = queue:in({presence, From}, StateData#state.room_queue),
|
|
|
|
StateData1 = StateData#state{room_queue = RoomQueue},
|
|
|
|
if
|
|
|
|
RoomQueueEmpty ->
|
|
|
|
StateData2 = prepare_room_queue(StateData1),
|
|
|
|
{next_state, normal_state, StateData2};
|
|
|
|
true ->
|
|
|
|
{next_state, normal_state, StateData1}
|
|
|
|
end;
|
2007-09-01 23:05:04 +02:00
|
|
|
handle_info({process_user_message, From}, normal_state = _StateName, StateData) ->
|
2008-04-27 21:06:34 +02:00
|
|
|
RoomQueueEmpty = queue:is_empty(StateData#state.room_queue),
|
|
|
|
RoomQueue = queue:in({message, From}, StateData#state.room_queue),
|
|
|
|
StateData1 = StateData#state{room_queue = RoomQueue},
|
|
|
|
if
|
|
|
|
RoomQueueEmpty ->
|
|
|
|
StateData2 = prepare_room_queue(StateData1),
|
|
|
|
{next_state, normal_state, StateData2};
|
|
|
|
true ->
|
|
|
|
{next_state, normal_state, StateData1}
|
|
|
|
end;
|
2007-09-01 23:05:04 +02:00
|
|
|
handle_info(process_room_queue, normal_state = StateName, StateData) ->
|
|
|
|
case queue:out(StateData#state.room_queue) of
|
|
|
|
{{value, {message, From}}, RoomQueue} ->
|
|
|
|
Activity = get_user_activity(From, StateData),
|
|
|
|
Packet = Activity#activity.message,
|
|
|
|
NewActivity = Activity#activity{message = undefined},
|
|
|
|
StateData1 =
|
2008-12-01 16:53:30 +01:00
|
|
|
store_user_activity(
|
|
|
|
From, NewActivity, StateData),
|
|
|
|
StateData2 =
|
|
|
|
StateData1#state{
|
2007-09-01 23:05:04 +02:00
|
|
|
room_queue = RoomQueue},
|
2008-12-01 16:53:30 +01:00
|
|
|
StateData3 = prepare_room_queue(StateData2),
|
|
|
|
process_groupchat_message(From, Packet, StateData3);
|
2007-09-01 23:05:04 +02:00
|
|
|
{{value, {presence, From}}, RoomQueue} ->
|
|
|
|
Activity = get_user_activity(From, StateData),
|
|
|
|
{Nick, Packet} = Activity#activity.presence,
|
|
|
|
NewActivity = Activity#activity{presence = undefined},
|
|
|
|
StateData1 =
|
2008-12-01 16:53:30 +01:00
|
|
|
store_user_activity(
|
|
|
|
From, NewActivity, StateData),
|
|
|
|
StateData2 =
|
|
|
|
StateData1#state{
|
2007-09-01 23:05:04 +02:00
|
|
|
room_queue = RoomQueue},
|
2008-12-01 16:53:30 +01:00
|
|
|
StateData3 = prepare_room_queue(StateData2),
|
|
|
|
process_presence(From, Nick, Packet, StateData3);
|
2007-09-01 23:05:04 +02:00
|
|
|
{empty, _} ->
|
|
|
|
{next_state, StateName, StateData}
|
|
|
|
end;
|
2009-05-26 19:20:09 +02:00
|
|
|
handle_info({captcha_succeed, From}, normal_state, StateData) ->
|
|
|
|
NewState = case ?DICT:find(From, StateData#state.robots) of
|
|
|
|
{ok, {Nick, Packet}} ->
|
|
|
|
Robots = ?DICT:store(From, passed, StateData#state.robots),
|
|
|
|
add_new_user(From, Nick, Packet, StateData#state{robots=Robots});
|
|
|
|
_ ->
|
|
|
|
StateData
|
|
|
|
end,
|
|
|
|
{next_state, normal_state, NewState};
|
|
|
|
handle_info({captcha_failed, From}, normal_state, StateData) ->
|
|
|
|
NewState = case ?DICT:find(From, StateData#state.robots) of
|
|
|
|
{ok, {Nick, Packet}} ->
|
|
|
|
Robots = ?DICT:erase(From, StateData#state.robots),
|
|
|
|
Err = exmpp_stanza:reply_with_error(
|
|
|
|
Packet, ?ERR(Packet, 'not-authorized', undefined, "")),
|
|
|
|
ejabberd_router:route( % TODO: s/Nick/""/
|
|
|
|
jid_replace_resource(
|
|
|
|
StateData#state.jid, Nick),
|
|
|
|
From, Err),
|
|
|
|
StateData#state{robots=Robots};
|
|
|
|
_ ->
|
|
|
|
StateData
|
|
|
|
end,
|
|
|
|
{next_state, normal_state, NewState};
|
2004-05-17 22:36:41 +02:00
|
|
|
handle_info(_Info, StateName, StateData) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
{next_state, StateName, StateData}.
|
|
|
|
|
|
|
|
%%----------------------------------------------------------------------
|
|
|
|
%% Func: terminate/3
|
|
|
|
%% Purpose: Shutdown the fsm
|
|
|
|
%% Returns: any
|
|
|
|
%%----------------------------------------------------------------------
|
2009-12-30 02:29:02 +01:00
|
|
|
terminate(Reason, _StateName, StateData) ->
|
2009-12-29 15:44:12 +01:00
|
|
|
?INFO_MSG("Stopping MUC room ~s@~s",
|
|
|
|
[StateData#state.room, StateData#state.host]),
|
2009-12-30 02:29:02 +01:00
|
|
|
ReasonT = case Reason of
|
|
|
|
shutdown -> "You are being removed from the room because"
|
|
|
|
" of a system shutdown";
|
|
|
|
_ -> atom_to_list(Reason)
|
|
|
|
end,
|
|
|
|
ReasonEl = #xmlel{name = 'reason', children = [#xmlcdata{cdata = ReasonT}]},
|
|
|
|
ItemAttrs = [?XMLATTR('affiliation', <<"none">>),
|
|
|
|
?XMLATTR('role', <<"none">>)],
|
|
|
|
XEls = [#xmlel{ns = ?NS_MUC_USER, name = 'item',
|
|
|
|
attrs = ItemAttrs,
|
|
|
|
children = [ReasonEl]},
|
|
|
|
#xmlel{ns = ?NS_MUC_USER, name = 'status',
|
|
|
|
attrs = [?XMLATTR('code', <<"332">>)]}],
|
|
|
|
Packet = #xmlel{ns = ?NS_JABBER_CLIENT,
|
|
|
|
name = 'presence',
|
|
|
|
attrs = [?XMLATTR('type', <<"unavailable">>)],
|
|
|
|
children = [#xmlel{ns = ?NS_MUC_USER, name = 'x',
|
|
|
|
children = XEls}
|
|
|
|
]},
|
2007-12-03 11:47:42 +01:00
|
|
|
?DICT:fold(
|
2009-12-30 02:29:02 +01:00
|
|
|
fun(LJID, Info, _) ->
|
|
|
|
Nick = Info#user.nick,
|
|
|
|
case Reason of
|
|
|
|
shutdown ->
|
|
|
|
ejabberd_router:route(
|
|
|
|
exmpp_jid:full(StateData#state.jid, Nick),
|
|
|
|
Info#user.jid,
|
|
|
|
Packet);
|
|
|
|
_ -> ok
|
|
|
|
end,
|
|
|
|
tab_remove_online_user(LJID, StateData)
|
2007-12-03 11:47:42 +01:00
|
|
|
end, [], StateData#state.users),
|
2009-12-29 15:44:12 +01:00
|
|
|
add_to_log(room_existence, stopped, StateData),
|
2009-01-15 20:41:06 +01:00
|
|
|
mod_muc:room_destroyed(StateData#state.host, StateData#state.room, self(),
|
2005-06-20 05:18:13 +02:00
|
|
|
StateData#state.server_host),
|
2003-03-23 21:08:44 +01:00
|
|
|
ok.
|
|
|
|
|
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
%%% Internal functions
|
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
|
|
|
|
route(Pid, From, ToNick, Packet) ->
|
|
|
|
gen_fsm:send_event(Pid, {route, From, ToNick, Packet}).
|
|
|
|
|
2008-12-01 16:01:27 +01:00
|
|
|
process_groupchat_message(From, #xmlel{name = 'message'} = Packet,
|
2007-06-25 18:43:42 +02:00
|
|
|
StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
Lang = exmpp_stanza:get_lang(Packet),
|
2008-06-13 20:55:26 +02:00
|
|
|
case is_user_online(From, StateData) orelse
|
|
|
|
is_user_allowed_message_nonparticipant(From, StateData) of
|
2007-09-01 23:05:04 +02:00
|
|
|
true ->
|
2008-06-13 20:55:26 +02:00
|
|
|
{FromNick, Role} = get_participant_data(From, StateData),
|
2007-09-01 23:05:04 +02:00
|
|
|
if
|
2008-02-14 12:25:39 +01:00
|
|
|
(Role == moderator) or (Role == participant)
|
|
|
|
or ((StateData#state.config)#config.moderated == false) ->
|
2007-09-01 23:05:04 +02:00
|
|
|
{NewStateData1, IsAllowed} =
|
|
|
|
case check_subject(Packet) of
|
|
|
|
false ->
|
|
|
|
{StateData, true};
|
|
|
|
Subject ->
|
|
|
|
case can_change_subject(Role,
|
|
|
|
StateData) of
|
|
|
|
true ->
|
|
|
|
NSD =
|
|
|
|
StateData#state{
|
|
|
|
subject = Subject,
|
|
|
|
subject_author =
|
|
|
|
FromNick},
|
|
|
|
case (NSD#state.config)#config.persistent of
|
|
|
|
true ->
|
|
|
|
mod_muc:store_room(
|
|
|
|
NSD#state.host,
|
|
|
|
NSD#state.room,
|
|
|
|
make_opts(NSD));
|
|
|
|
_ ->
|
|
|
|
ok
|
|
|
|
end,
|
|
|
|
{NSD, true};
|
|
|
|
_ ->
|
|
|
|
{StateData, false}
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
case IsAllowed of
|
|
|
|
true ->
|
|
|
|
lists:foreach(
|
|
|
|
fun({_LJID, Info}) ->
|
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(
|
2007-09-01 23:05:04 +02:00
|
|
|
StateData#state.jid,
|
|
|
|
FromNick),
|
|
|
|
Info#user.jid,
|
|
|
|
Packet)
|
|
|
|
end,
|
|
|
|
?DICT:to_list(StateData#state.users)),
|
|
|
|
NewStateData2 =
|
|
|
|
add_message_to_history(FromNick,
|
2009-06-30 18:55:26 +02:00
|
|
|
From,
|
2007-09-01 23:05:04 +02:00
|
|
|
Packet,
|
|
|
|
NewStateData1),
|
|
|
|
{next_state, normal_state, NewStateData2};
|
|
|
|
_ ->
|
|
|
|
Err =
|
|
|
|
case (StateData#state.config)#config.allow_change_subj of
|
2007-06-25 18:43:42 +02:00
|
|
|
true ->
|
2008-12-01 16:01:27 +01:00
|
|
|
exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'forbidden',
|
|
|
|
{Lang, translate:translate(Lang,
|
|
|
|
"Only moderators and participants "
|
2009-04-08 21:04:13 +02:00
|
|
|
"are allowed to change the subject in this room")}));
|
2007-06-25 18:43:42 +02:00
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'forbidden',
|
|
|
|
{Lang, translate:translate(Lang,
|
|
|
|
"Only moderators "
|
2009-04-08 21:04:13 +02:00
|
|
|
"are allowed to change the subject in this room")}))
|
2007-06-25 18:43:42 +02:00
|
|
|
end,
|
2007-09-01 23:05:04 +02:00
|
|
|
ejabberd_router:route(
|
|
|
|
StateData#state.jid,
|
|
|
|
From,
|
2008-12-01 16:01:27 +01:00
|
|
|
Err),
|
2007-09-01 23:05:04 +02:00
|
|
|
{next_state, normal_state, StateData}
|
|
|
|
end;
|
2007-06-25 18:43:42 +02:00
|
|
|
true ->
|
2007-09-01 23:05:04 +02:00
|
|
|
ErrText = "Visitors are not allowed to send messages to all occupants",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'forbidden',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})),
|
2007-06-25 18:43:42 +02:00
|
|
|
ejabberd_router:route(
|
|
|
|
StateData#state.jid,
|
2007-09-01 23:05:04 +02:00
|
|
|
From, Err),
|
2007-06-25 18:43:42 +02:00
|
|
|
{next_state, normal_state, StateData}
|
|
|
|
end;
|
2007-09-01 23:05:04 +02:00
|
|
|
false ->
|
|
|
|
ErrText = "Only occupants are allowed to send messages to the conference",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'not-acceptable',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})),
|
2007-09-01 23:05:04 +02:00
|
|
|
ejabberd_router:route(StateData#state.jid, From, Err),
|
2007-06-25 18:43:42 +02:00
|
|
|
{next_state, normal_state, StateData}
|
|
|
|
end.
|
|
|
|
|
2008-06-13 20:55:26 +02:00
|
|
|
%% @doc Check if this non participant can send message to room.
|
|
|
|
%%
|
|
|
|
%% XEP-0045 v1.23:
|
|
|
|
%% 7.9 Sending a Message to All Occupants
|
|
|
|
%% an implementation MAY allow users with certain privileges
|
|
|
|
%% (e.g., a room owner, room admin, or service-level admin)
|
|
|
|
%% to send messages to the room even if those users are not occupants.
|
|
|
|
%%
|
|
|
|
%% Check the mod_muc option access_message_nonparticipant and wether this JID
|
|
|
|
%% is allowed or denied
|
|
|
|
is_user_allowed_message_nonparticipant(JID, StateData) ->
|
2008-09-16 16:39:57 +02:00
|
|
|
case get_service_affiliation(JID, StateData) of
|
|
|
|
owner ->
|
2008-06-13 20:55:26 +02:00
|
|
|
true;
|
|
|
|
_ -> false
|
|
|
|
end.
|
|
|
|
|
|
|
|
%% @doc Get information of this participant, or default values.
|
|
|
|
%% If the JID is not a participant, return values for a service message.
|
|
|
|
get_participant_data(From, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case ?DICT:find(jlib:short_prepd_jid(From), StateData#state.users) of
|
2008-06-13 20:55:26 +02:00
|
|
|
{ok, #user{nick = FromNick, role = Role}} ->
|
|
|
|
{FromNick, Role};
|
|
|
|
error ->
|
2009-01-15 20:41:06 +01:00
|
|
|
{<<>>, moderator}
|
2008-06-13 20:55:26 +02:00
|
|
|
end.
|
|
|
|
|
|
|
|
|
2008-12-01 16:01:27 +01:00
|
|
|
process_presence(From, Nick, #xmlel{name = 'presence'} = Packet,
|
2007-06-25 18:43:42 +02:00
|
|
|
StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
Type = exmpp_presence:get_type(Packet),
|
|
|
|
Lang = exmpp_stanza:get_lang(Packet),
|
2007-06-25 18:43:42 +02:00
|
|
|
StateData1 =
|
|
|
|
case Type of
|
2008-12-01 16:01:27 +01:00
|
|
|
unavailable ->
|
2007-06-25 18:43:42 +02:00
|
|
|
case is_user_online(From, StateData) of
|
|
|
|
true ->
|
|
|
|
NewState =
|
|
|
|
add_user_presence_un(From, Packet, StateData),
|
|
|
|
send_new_presence(From, NewState),
|
2008-12-01 16:01:27 +01:00
|
|
|
Reason = case exmpp_xml:get_element(Packet, 'status') of
|
|
|
|
undefined -> <<>>;
|
|
|
|
Status_el -> exmpp_xml:get_cdata(Status_el)
|
2007-06-25 18:43:42 +02:00
|
|
|
end,
|
|
|
|
remove_online_user(From, NewState, Reason);
|
|
|
|
_ ->
|
|
|
|
StateData
|
|
|
|
end;
|
2008-12-01 16:01:27 +01:00
|
|
|
error ->
|
2007-06-25 18:43:42 +02:00
|
|
|
case is_user_online(From, StateData) of
|
|
|
|
true ->
|
2008-02-09 11:38:47 +01:00
|
|
|
ErrorText = "This participant is kicked from the room because "
|
|
|
|
"he sent an error presence",
|
|
|
|
expulse_participant(Packet, From, StateData,
|
|
|
|
translate:translate(Lang, ErrorText));
|
2007-06-25 18:43:42 +02:00
|
|
|
_ ->
|
|
|
|
StateData
|
|
|
|
end;
|
2008-12-17 17:24:15 +01:00
|
|
|
'available' ->
|
2007-06-25 18:43:42 +02:00
|
|
|
case is_user_online(From, StateData) of
|
|
|
|
true ->
|
|
|
|
case is_nick_change(From, Nick, StateData) of
|
|
|
|
true ->
|
|
|
|
case {is_nick_exists(Nick, StateData),
|
|
|
|
mod_muc:can_use_nick(
|
2009-01-15 20:41:06 +01:00
|
|
|
StateData#state.host, From, Nick),
|
2008-12-01 16:01:27 +01:00
|
|
|
{(StateData#state.config)#config.allow_visitor_nickchange,
|
|
|
|
is_visitor(From, StateData)}} of
|
|
|
|
{_, _, {false, true}} ->
|
2008-09-16 16:39:57 +02:00
|
|
|
ErrText = "Visitors are not allowed to change their nicknames in this room",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'not-allowed',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})),
|
2008-09-16 16:39:57 +02:00
|
|
|
ejabberd_router:route(
|
|
|
|
% TODO: s/Nick/""/
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(
|
2008-09-16 16:39:57 +02:00
|
|
|
StateData#state.jid,
|
|
|
|
Nick),
|
|
|
|
From, Err),
|
|
|
|
StateData;
|
|
|
|
{true, _, _} ->
|
2008-12-01 16:01:27 +01:00
|
|
|
Lang = exmpp_stanza:get_lang(Packet),
|
2009-04-08 21:04:13 +02:00
|
|
|
ErrText = "That nickname is already in use by another occupant",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'conflict',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})),
|
2007-06-25 18:43:42 +02:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(
|
2007-06-25 18:43:42 +02:00
|
|
|
StateData#state.jid,
|
|
|
|
Nick), % TODO: s/Nick/""/
|
|
|
|
From, Err),
|
|
|
|
StateData;
|
2008-09-16 16:39:57 +02:00
|
|
|
{_, false, _} ->
|
2009-04-08 21:04:13 +02:00
|
|
|
ErrText = "That nickname is registered by another person",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns, 'conflict',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})),
|
2007-06-25 18:43:42 +02:00
|
|
|
ejabberd_router:route(
|
|
|
|
% TODO: s/Nick/""/
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(
|
2007-06-25 18:43:42 +02:00
|
|
|
StateData#state.jid,
|
|
|
|
Nick),
|
|
|
|
From, Err),
|
|
|
|
StateData;
|
|
|
|
_ ->
|
|
|
|
change_nick(From, Nick, StateData)
|
|
|
|
end;
|
2008-09-16 16:39:57 +02:00
|
|
|
_NotNickChange ->
|
|
|
|
Stanza = case {(StateData#state.config)#config.allow_visitor_status,
|
|
|
|
is_visitor(From, StateData)} of
|
|
|
|
{false, true} ->
|
|
|
|
strip_status(Packet);
|
|
|
|
_Allowed ->
|
|
|
|
Packet
|
|
|
|
end,
|
|
|
|
NewState = add_user_presence(From, Stanza, StateData),
|
|
|
|
send_new_presence(From, NewState),
|
|
|
|
NewState
|
2007-06-25 18:43:42 +02:00
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
add_new_user(From, Nick, Packet, StateData)
|
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
StateData
|
|
|
|
end,
|
|
|
|
case (not (StateData1#state.config)#config.persistent) andalso
|
|
|
|
(?DICT:to_list(StateData1#state.users) == []) of
|
|
|
|
true ->
|
2008-01-16 11:08:17 +01:00
|
|
|
?INFO_MSG("Destroyed MUC room ~s because it's temporary and empty",
|
2009-06-01 18:59:08 +02:00
|
|
|
[exmpp_jid:to_binary(StateData#state.jid)]),
|
2009-12-29 15:44:12 +01:00
|
|
|
add_to_log(room_existence, destroyed, StateData),
|
2007-06-25 18:43:42 +02:00
|
|
|
{stop, normal, StateData1};
|
|
|
|
_ ->
|
|
|
|
{next_state, normal_state, StateData1}
|
|
|
|
end.
|
2003-03-23 21:08:44 +01:00
|
|
|
|
|
|
|
is_user_online(JID, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_jid(JID),
|
2003-03-23 21:08:44 +01:00
|
|
|
?DICT:is_key(LJID, StateData#state.users).
|
|
|
|
|
2009-12-01 21:02:16 +01:00
|
|
|
%%%
|
|
|
|
%%% Handle IQ queries of vCard
|
|
|
|
%%%
|
|
|
|
get_stanza_id(Packet) ->
|
|
|
|
case exmpp_stanza:get_id(Packet) of
|
|
|
|
Id when is_binary(Id) -> binary_to_list(Id);
|
|
|
|
undefined -> ""
|
|
|
|
end.
|
|
|
|
|
|
|
|
is_user_online_iq(StanzaId, JID, StateData) ->
|
|
|
|
case exmpp_jid:resource(JID) of
|
|
|
|
undefined -> is_user_online_iq2(StanzaId, JID, StateData);
|
|
|
|
_ -> {is_user_online(JID, StateData), StanzaId, JID}
|
|
|
|
end.
|
|
|
|
|
|
|
|
is_user_online_iq2(StanzaId, JID, StateData) ->
|
|
|
|
try stanzaid_unpack(StanzaId) of
|
|
|
|
{OriginalId, Resource} ->
|
|
|
|
JIDWithResource = jid_replace_resource(JID, Resource),
|
|
|
|
{is_user_online(JIDWithResource, StateData),
|
|
|
|
OriginalId, JIDWithResource}
|
|
|
|
catch
|
|
|
|
_:_ ->
|
|
|
|
{is_user_online(JID, StateData), StanzaId, JID}
|
|
|
|
end.
|
|
|
|
|
|
|
|
handle_iq_vcard(FromFull, ToJID, StanzaId, NewId, Packet) ->
|
|
|
|
ToBareJID = exmpp_jid:bare(ToJID),
|
|
|
|
IsToJIDBare = exmpp_jid:full_compare(ToJID, ToBareJID),
|
|
|
|
IQ = exmpp_iq:xmlel_to_iq(Packet),
|
|
|
|
handle_iq_vcard2(FromFull, ToJID, ToBareJID, IsToJIDBare, StanzaId, NewId, IQ, Packet).
|
|
|
|
handle_iq_vcard2(_FromFull, ToJID, ToBareJID, IsToJIDBare, StanzaId, _NewId,
|
|
|
|
#iq{type = get, ns = ?NS_VCARD}, Packet)
|
|
|
|
when IsToJIDBare == false ->
|
|
|
|
{ToBareJID, change_stanzaid(StanzaId, ToJID, Packet)};
|
|
|
|
handle_iq_vcard2(_FromFull, ToJID, _ToBareJID, _IsToJIDBare, _StanzaId, NewId, _IQ, Packet) ->
|
|
|
|
{ToJID, change_stanzaid(NewId, Packet)}.
|
|
|
|
|
|
|
|
stanzaid_pack(OriginalId, Resource) ->
|
|
|
|
"berd"++base64:encode_to_string("ejab\0" ++ OriginalId ++ "\0" ++ Resource).
|
|
|
|
stanzaid_unpack("berd"++StanzaIdBase64) ->
|
|
|
|
StanzaId = base64:decode_to_string(StanzaIdBase64),
|
|
|
|
["ejab", OriginalId, Resource] = string:tokens(StanzaId, "\0"),
|
|
|
|
{OriginalId, Resource}.
|
|
|
|
|
|
|
|
change_stanzaid(NewId, Packet) ->
|
|
|
|
exmpp_stanza:set_id(Packet, NewId).
|
|
|
|
change_stanzaid(PreviousId, ToJID, Packet) ->
|
|
|
|
NewId = stanzaid_pack(PreviousId, exmpp_jid:resource_as_list(ToJID)),
|
|
|
|
change_stanzaid(NewId, Packet).
|
|
|
|
%%%
|
|
|
|
%%%
|
|
|
|
|
2009-01-09 20:18:46 +01:00
|
|
|
role_to_binary(Role) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
case Role of
|
2009-01-09 20:18:46 +01:00
|
|
|
moderator -> <<"moderator">>;
|
|
|
|
participant -> <<"participant">>;
|
|
|
|
visitor -> <<"visitor">>;
|
|
|
|
none -> <<"none">>
|
2003-03-23 21:08:44 +01:00
|
|
|
end.
|
|
|
|
|
2009-01-09 20:18:46 +01:00
|
|
|
affiliation_to_binary(Affiliation) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
case Affiliation of
|
2009-01-09 20:18:46 +01:00
|
|
|
owner -> <<"owner">>;
|
|
|
|
admin -> <<"admin">>;
|
|
|
|
member -> <<"member">>;
|
|
|
|
outcast -> <<"outcast">>;
|
|
|
|
none -> <<"none">>
|
2003-03-23 21:08:44 +01:00
|
|
|
end.
|
|
|
|
|
2009-01-09 20:18:46 +01:00
|
|
|
binary_to_role(Role) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
case Role of
|
2009-01-09 20:18:46 +01:00
|
|
|
<<"moderator">> -> moderator;
|
|
|
|
<<"participant">> -> participant;
|
|
|
|
<<"visitor">> -> visitor;
|
|
|
|
<<"none">> -> none
|
2003-03-23 21:08:44 +01:00
|
|
|
end.
|
|
|
|
|
2009-01-09 20:18:46 +01:00
|
|
|
binary_to_affiliation(Affiliation) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
case Affiliation of
|
2009-01-09 20:18:46 +01:00
|
|
|
<<"owner">> -> owner;
|
|
|
|
<<"admin">> -> admin;
|
|
|
|
<<"member">> -> member;
|
|
|
|
<<"outcast">> -> outcast;
|
|
|
|
<<"none">> -> none
|
2003-03-23 21:08:44 +01:00
|
|
|
end.
|
|
|
|
|
2008-01-29 15:49:08 +01:00
|
|
|
%% Decide the fate of the message and its sender
|
|
|
|
%% Returns: continue_delivery | forget_message | {expulse_sender, Reason}
|
2008-12-01 16:01:27 +01:00
|
|
|
decide_fate_message(error, Packet, From, StateData) ->
|
2008-01-29 19:37:45 +01:00
|
|
|
%% Make a preliminary decision
|
2008-02-09 11:38:47 +01:00
|
|
|
PD = case check_error_kick(Packet) of
|
2008-01-29 19:37:45 +01:00
|
|
|
%% If this is an error stanza and its condition matches a criteria
|
|
|
|
true ->
|
|
|
|
Reason = io_lib:format("This participant is considered a ghost and is expulsed: ~s",
|
2009-06-01 18:59:08 +02:00
|
|
|
[exmpp_jid:to_binary(From)]),
|
2008-01-29 19:37:45 +01:00
|
|
|
{expulse_sender, Reason};
|
|
|
|
false ->
|
2008-02-09 11:38:47 +01:00
|
|
|
continue_delivery
|
2008-01-29 19:37:45 +01:00
|
|
|
end,
|
|
|
|
case PD of
|
|
|
|
{expulse_sender, R} ->
|
2008-01-29 15:49:08 +01:00
|
|
|
case is_user_online(From, StateData) of
|
|
|
|
true ->
|
2008-01-29 19:37:45 +01:00
|
|
|
{expulse_sender, R};
|
2008-01-29 15:49:08 +01:00
|
|
|
false ->
|
|
|
|
forget_message
|
|
|
|
end;
|
2008-01-29 19:37:45 +01:00
|
|
|
Other ->
|
|
|
|
Other
|
2008-01-29 15:49:08 +01:00
|
|
|
end;
|
2008-01-29 19:37:45 +01:00
|
|
|
|
2008-01-29 15:49:08 +01:00
|
|
|
decide_fate_message(_, _, _, _) ->
|
|
|
|
continue_delivery.
|
|
|
|
|
|
|
|
%% Check if the elements of this error stanza indicate
|
|
|
|
%% that the sender is a dead participant.
|
|
|
|
%% If so, return true to kick the participant.
|
|
|
|
check_error_kick(Packet) ->
|
2008-02-09 11:38:47 +01:00
|
|
|
case get_error_condition(Packet) of
|
2008-12-01 16:01:27 +01:00
|
|
|
'gone' -> true;
|
|
|
|
'internal-server-error' -> true;
|
|
|
|
'item-not-found' -> true;
|
|
|
|
'jid-malformed' -> true;
|
|
|
|
'recipient-unavailable' -> true;
|
|
|
|
'redirect' -> true;
|
|
|
|
'remote-server-not-found' -> true;
|
|
|
|
'remote-server-timeout' -> true;
|
|
|
|
'service-unavailable' -> true;
|
2008-01-29 15:49:08 +01:00
|
|
|
_ -> false
|
|
|
|
end.
|
2003-03-23 21:08:44 +01:00
|
|
|
|
2008-02-09 11:38:47 +01:00
|
|
|
get_error_condition(Packet) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
try exmpp_stanza:get_condition(Packet) of
|
|
|
|
ErrorCondition -> ErrorCondition
|
|
|
|
catch
|
|
|
|
_:_ ->
|
|
|
|
'badformed-error-stanza'
|
2008-02-09 11:38:47 +01:00
|
|
|
end.
|
|
|
|
|
|
|
|
expulse_participant(Packet, From, StateData, Reason1) ->
|
|
|
|
ErrorCondition = get_error_condition(Packet),
|
|
|
|
Reason2 = io_lib:format(Reason1 ++ ": " ++ "~s", [ErrorCondition]),
|
|
|
|
NewState = add_user_presence_un(
|
|
|
|
From,
|
2008-12-01 16:01:27 +01:00
|
|
|
exmpp_presence:presence('unavailable',Reason2),
|
2008-02-09 11:38:47 +01:00
|
|
|
StateData),
|
|
|
|
send_new_presence(From, NewState),
|
|
|
|
remove_online_user(From, NewState).
|
|
|
|
|
2003-03-23 21:08:44 +01:00
|
|
|
|
|
|
|
set_affiliation(JID, Affiliation, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_bare_jid(JID),
|
2003-03-23 21:08:44 +01:00
|
|
|
Affiliations = case Affiliation of
|
|
|
|
none ->
|
|
|
|
?DICT:erase(LJID,
|
|
|
|
StateData#state.affiliations);
|
|
|
|
_ ->
|
|
|
|
?DICT:store(LJID,
|
|
|
|
Affiliation,
|
|
|
|
StateData#state.affiliations)
|
|
|
|
end,
|
|
|
|
StateData#state{affiliations = Affiliations}.
|
|
|
|
|
2005-05-04 01:07:14 +02:00
|
|
|
set_affiliation_and_reason(JID, Affiliation, Reason, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_bare_jid(JID),
|
2005-05-04 01:07:14 +02:00
|
|
|
Affiliations = case Affiliation of
|
|
|
|
none ->
|
|
|
|
?DICT:erase(LJID,
|
|
|
|
StateData#state.affiliations);
|
|
|
|
_ ->
|
|
|
|
?DICT:store(LJID,
|
|
|
|
{Affiliation, Reason},
|
|
|
|
StateData#state.affiliations)
|
|
|
|
end,
|
|
|
|
StateData#state{affiliations = Affiliations}.
|
|
|
|
|
2003-03-23 21:08:44 +01:00
|
|
|
get_affiliation(JID, StateData) ->
|
2007-06-20 13:25:19 +02:00
|
|
|
{_AccessRoute, _AccessCreate, AccessAdmin, _AccessPersistent} = StateData#state.access,
|
2005-05-04 01:07:14 +02:00
|
|
|
Res =
|
2005-06-20 05:18:13 +02:00
|
|
|
case acl:match_rule(StateData#state.server_host, AccessAdmin, JID) of
|
2005-05-04 01:07:14 +02:00
|
|
|
allow ->
|
|
|
|
owner;
|
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_jid(JID),
|
2005-05-04 01:07:14 +02:00
|
|
|
case ?DICT:find(LJID, StateData#state.affiliations) of
|
|
|
|
{ok, Affiliation} ->
|
|
|
|
Affiliation;
|
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID1 = jlib:short_prepd_bare_jid(JID),
|
2005-05-04 01:07:14 +02:00
|
|
|
case ?DICT:find(LJID1, StateData#state.affiliations) of
|
|
|
|
{ok, Affiliation} ->
|
|
|
|
Affiliation;
|
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID2 = setelement(1, LJID, undefined),
|
2005-05-04 01:07:14 +02:00
|
|
|
case ?DICT:find(LJID2, StateData#state.affiliations) of
|
|
|
|
{ok, Affiliation} ->
|
|
|
|
Affiliation;
|
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID3 = setelement(1,jlib:short_prepd_bare_jid(JID),undefined),
|
2005-05-04 01:07:14 +02:00
|
|
|
case ?DICT:find(LJID3, StateData#state.affiliations) of
|
|
|
|
{ok, Affiliation} ->
|
|
|
|
Affiliation;
|
|
|
|
_ ->
|
|
|
|
none
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
case Res of
|
|
|
|
{A, _Reason} ->
|
|
|
|
A;
|
2003-03-23 21:08:44 +01:00
|
|
|
_ ->
|
2005-05-04 01:07:14 +02:00
|
|
|
Res
|
2003-03-23 21:08:44 +01:00
|
|
|
end.
|
|
|
|
|
2007-08-29 19:54:45 +02:00
|
|
|
get_service_affiliation(JID, StateData) ->
|
|
|
|
{_AccessRoute, _AccessCreate, AccessAdmin, _AccessPersistent} =
|
|
|
|
StateData#state.access,
|
|
|
|
case acl:match_rule(StateData#state.server_host, AccessAdmin, JID) of
|
|
|
|
allow ->
|
|
|
|
owner;
|
|
|
|
_ ->
|
|
|
|
none
|
|
|
|
end.
|
|
|
|
|
2003-03-23 21:08:44 +01:00
|
|
|
set_role(JID, Role, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_jid(JID),
|
2003-04-13 21:22:46 +02:00
|
|
|
LJIDs = case LJID of
|
2008-12-01 16:01:27 +01:00
|
|
|
{U, S, undefined} ->
|
2003-04-13 21:22:46 +02:00
|
|
|
?DICT:fold(
|
|
|
|
fun(J, _, Js) ->
|
|
|
|
case J of
|
|
|
|
{U, S, _} ->
|
|
|
|
[J | Js];
|
|
|
|
_ ->
|
|
|
|
Js
|
|
|
|
end
|
|
|
|
end, [], StateData#state.users);
|
|
|
|
_ ->
|
|
|
|
case ?DICT:is_key(LJID, StateData#state.users) of
|
|
|
|
true ->
|
|
|
|
[LJID];
|
|
|
|
_ ->
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
end,
|
2003-03-23 21:08:44 +01:00
|
|
|
Users = case Role of
|
|
|
|
none ->
|
2003-04-13 21:22:46 +02:00
|
|
|
lists:foldl(fun(J, Us) ->
|
|
|
|
?DICT:erase(J,
|
|
|
|
Us)
|
|
|
|
end, StateData#state.users, LJIDs);
|
2003-03-23 21:08:44 +01:00
|
|
|
_ ->
|
2003-04-13 21:22:46 +02:00
|
|
|
lists:foldl(fun(J, Us) ->
|
|
|
|
{ok, User} = ?DICT:find(J, Us),
|
|
|
|
?DICT:store(J,
|
|
|
|
User#user{role = Role},
|
|
|
|
Us)
|
|
|
|
end, StateData#state.users, LJIDs)
|
2003-03-23 21:08:44 +01:00
|
|
|
end,
|
|
|
|
StateData#state{users = Users}.
|
|
|
|
|
|
|
|
get_role(JID, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_jid(JID),
|
2003-03-23 21:08:44 +01:00
|
|
|
case ?DICT:find(LJID, StateData#state.users) of
|
|
|
|
{ok, #user{role = Role}} ->
|
|
|
|
Role;
|
|
|
|
_ ->
|
|
|
|
none
|
|
|
|
end.
|
|
|
|
|
|
|
|
get_default_role(Affiliation, StateData) ->
|
|
|
|
case Affiliation of
|
|
|
|
owner -> moderator;
|
|
|
|
admin -> moderator;
|
|
|
|
member -> participant;
|
|
|
|
outcast -> none;
|
2003-03-25 22:03:35 +01:00
|
|
|
none ->
|
2003-04-13 21:22:46 +02:00
|
|
|
case (StateData#state.config)#config.members_only of
|
2003-03-25 22:03:35 +01:00
|
|
|
true ->
|
2003-04-13 21:22:46 +02:00
|
|
|
none;
|
2003-03-25 22:03:35 +01:00
|
|
|
_ ->
|
2003-04-13 21:22:46 +02:00
|
|
|
case (StateData#state.config)#config.members_by_default of
|
|
|
|
true ->
|
|
|
|
participant;
|
|
|
|
_ ->
|
|
|
|
visitor
|
|
|
|
end
|
2003-03-25 22:03:35 +01:00
|
|
|
end
|
2003-03-23 21:08:44 +01:00
|
|
|
end.
|
|
|
|
|
2008-09-16 16:39:57 +02:00
|
|
|
is_visitor(Jid, StateData) ->
|
|
|
|
get_role(Jid, StateData) =:= visitor.
|
|
|
|
|
2007-08-29 19:54:45 +02:00
|
|
|
get_max_users(StateData) ->
|
|
|
|
MaxUsers = (StateData#state.config)#config.max_users,
|
|
|
|
ServiceMaxUsers = get_service_max_users(StateData),
|
|
|
|
if
|
|
|
|
MaxUsers =< ServiceMaxUsers -> MaxUsers;
|
|
|
|
true -> ServiceMaxUsers
|
|
|
|
end.
|
|
|
|
|
|
|
|
get_service_max_users(StateData) ->
|
|
|
|
gen_mod:get_module_opt(StateData#state.server_host,
|
|
|
|
mod_muc, max_users, ?MAX_USERS_DEFAULT).
|
|
|
|
|
|
|
|
get_max_users_admin_threshold(StateData) ->
|
|
|
|
gen_mod:get_module_opt(StateData#state.server_host,
|
|
|
|
mod_muc, max_users_admin_threshold, 5).
|
|
|
|
|
2007-09-01 23:05:04 +02:00
|
|
|
get_user_activity(JID, StateData) ->
|
2008-12-01 16:53:30 +01:00
|
|
|
case treap:lookup(jlib:short_prepd_jid(JID),
|
|
|
|
StateData#state.activity) of
|
|
|
|
{ok, _P, A} -> A;
|
2007-09-01 23:05:04 +02:00
|
|
|
error ->
|
|
|
|
MessageShaper =
|
|
|
|
shaper:new(gen_mod:get_module_opt(
|
|
|
|
StateData#state.server_host,
|
|
|
|
mod_muc, user_message_shaper, none)),
|
|
|
|
PresenceShaper =
|
|
|
|
shaper:new(gen_mod:get_module_opt(
|
|
|
|
StateData#state.server_host,
|
|
|
|
mod_muc, user_presence_shaper, none)),
|
|
|
|
#activity{message_shaper = MessageShaper,
|
|
|
|
presence_shaper = PresenceShaper}
|
|
|
|
end.
|
|
|
|
|
2008-12-01 16:53:30 +01:00
|
|
|
store_user_activity(JID, UserActivity, StateData) ->
|
|
|
|
MinMessageInterval =
|
|
|
|
gen_mod:get_module_opt(
|
|
|
|
StateData#state.server_host,
|
|
|
|
mod_muc, min_message_interval, 0),
|
|
|
|
MinPresenceInterval =
|
|
|
|
gen_mod:get_module_opt(
|
|
|
|
StateData#state.server_host,
|
|
|
|
mod_muc, min_presence_interval, 0),
|
|
|
|
Key = jlib:short_prepd_jid(JID),
|
|
|
|
Now = now_to_usec(now()),
|
|
|
|
Activity1 = clean_treap(StateData#state.activity, {1, -Now}),
|
|
|
|
Activity =
|
|
|
|
case treap:lookup(Key, Activity1) of
|
|
|
|
{ok, _P, _A} ->
|
|
|
|
treap:delete(Key, Activity1);
|
|
|
|
error ->
|
|
|
|
Activity1
|
|
|
|
end,
|
|
|
|
StateData1 =
|
|
|
|
case (MinMessageInterval == 0) andalso
|
|
|
|
(MinPresenceInterval == 0) andalso
|
|
|
|
(UserActivity#activity.message_shaper == none) andalso
|
|
|
|
(UserActivity#activity.presence_shaper == none) andalso
|
|
|
|
(UserActivity#activity.message == undefined) andalso
|
|
|
|
(UserActivity#activity.presence == undefined) of
|
|
|
|
true ->
|
|
|
|
StateData#state{activity = Activity};
|
|
|
|
false ->
|
|
|
|
case (UserActivity#activity.message == undefined) andalso
|
|
|
|
(UserActivity#activity.presence == undefined) of
|
|
|
|
true ->
|
|
|
|
{_, MessageShaperInterval} =
|
|
|
|
shaper:update(UserActivity#activity.message_shaper,
|
|
|
|
100000),
|
|
|
|
{_, PresenceShaperInterval} =
|
|
|
|
shaper:update(UserActivity#activity.presence_shaper,
|
|
|
|
100000),
|
|
|
|
Delay = lists:max([MessageShaperInterval,
|
|
|
|
PresenceShaperInterval,
|
|
|
|
MinMessageInterval * 1000,
|
|
|
|
MinPresenceInterval * 1000]) * 1000,
|
|
|
|
Priority = {1, -(Now + Delay)},
|
|
|
|
StateData#state{
|
|
|
|
activity = treap:insert(
|
|
|
|
Key,
|
|
|
|
Priority,
|
|
|
|
UserActivity,
|
|
|
|
Activity)};
|
|
|
|
false ->
|
|
|
|
Priority = {0, 0},
|
|
|
|
StateData#state{
|
|
|
|
activity = treap:insert(
|
|
|
|
Key,
|
|
|
|
Priority,
|
|
|
|
UserActivity,
|
|
|
|
Activity)}
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
StateData1.
|
|
|
|
|
|
|
|
clean_treap(Treap, CleanPriority) ->
|
|
|
|
case treap:is_empty(Treap) of
|
|
|
|
true ->
|
|
|
|
Treap;
|
|
|
|
false ->
|
|
|
|
{_Key, Priority, _Value} = treap:get_root(Treap),
|
|
|
|
if
|
|
|
|
Priority > CleanPriority ->
|
|
|
|
clean_treap(treap:delete_root(Treap), CleanPriority);
|
|
|
|
true ->
|
|
|
|
Treap
|
|
|
|
end
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
2007-09-01 23:05:04 +02:00
|
|
|
prepare_room_queue(StateData) ->
|
|
|
|
case queue:out(StateData#state.room_queue) of
|
|
|
|
{{value, {message, From}}, _RoomQueue} ->
|
|
|
|
Activity = get_user_activity(From, StateData),
|
|
|
|
Packet = Activity#activity.message,
|
2009-02-18 14:48:06 +01:00
|
|
|
Size = erlang:iolist_size(exmpp_xml:document_to_iolist(Packet)),
|
2007-09-01 23:05:04 +02:00
|
|
|
{RoomShaper, RoomShaperInterval} =
|
|
|
|
shaper:update(StateData#state.room_shaper, Size),
|
|
|
|
erlang:send_after(
|
|
|
|
RoomShaperInterval, self(),
|
|
|
|
process_room_queue),
|
|
|
|
StateData#state{
|
|
|
|
room_shaper = RoomShaper};
|
|
|
|
{{value, {presence, From}}, _RoomQueue} ->
|
|
|
|
Activity = get_user_activity(From, StateData),
|
|
|
|
{_Nick, Packet} = Activity#activity.presence,
|
2009-01-15 16:21:54 +01:00
|
|
|
Size = erlang:iolist_size(exmpp_xml:document_to_iolist(Packet)),
|
2007-09-01 23:05:04 +02:00
|
|
|
{RoomShaper, RoomShaperInterval} =
|
|
|
|
shaper:update(StateData#state.room_shaper, Size),
|
|
|
|
erlang:send_after(
|
|
|
|
RoomShaperInterval, self(),
|
|
|
|
process_room_queue),
|
|
|
|
StateData#state{
|
|
|
|
room_shaper = RoomShaper};
|
|
|
|
{empty, _} ->
|
|
|
|
StateData
|
|
|
|
end.
|
|
|
|
|
2003-03-23 21:08:44 +01:00
|
|
|
|
|
|
|
add_online_user(JID, Nick, Role, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_jid(JID),
|
2003-03-23 21:08:44 +01:00
|
|
|
Users = ?DICT:store(LJID,
|
|
|
|
#user{jid = JID,
|
|
|
|
nick = Nick,
|
|
|
|
role = Role},
|
|
|
|
StateData#state.users),
|
2006-03-14 05:26:15 +01:00
|
|
|
add_to_log(join, Nick, StateData),
|
2007-12-03 11:47:42 +01:00
|
|
|
tab_add_online_user(JID, StateData),
|
2003-03-23 21:08:44 +01:00
|
|
|
StateData#state{users = Users}.
|
|
|
|
|
|
|
|
remove_online_user(JID, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
remove_online_user(JID, StateData, <<>>).
|
2006-03-14 05:26:15 +01:00
|
|
|
|
|
|
|
remove_online_user(JID, StateData, Reason) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_jid(JID),
|
2006-03-14 05:26:15 +01:00
|
|
|
{ok, #user{nick = Nick}} =
|
|
|
|
?DICT:find(LJID, StateData#state.users),
|
|
|
|
add_to_log(leave, {Nick, Reason}, StateData),
|
2007-12-03 11:47:42 +01:00
|
|
|
tab_remove_online_user(JID, StateData),
|
2003-03-23 21:08:44 +01:00
|
|
|
Users = ?DICT:erase(LJID, StateData#state.users),
|
|
|
|
StateData#state{users = Users}.
|
|
|
|
|
|
|
|
|
2008-12-01 16:01:27 +01:00
|
|
|
filter_presence(#xmlel{name = 'presence'} = Packet) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
FEls = lists:filter(
|
|
|
|
fun(El) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case El of
|
|
|
|
#xmlel{ns = XMLNS} ->
|
|
|
|
case atom_to_list(XMLNS) of
|
|
|
|
?NS_MUC_s ++ _ ->
|
2006-01-19 03:17:31 +01:00
|
|
|
false;
|
2003-03-23 21:08:44 +01:00
|
|
|
_ ->
|
2006-01-19 03:17:31 +01:00
|
|
|
true
|
2003-03-23 21:08:44 +01:00
|
|
|
end
|
|
|
|
end
|
2008-12-01 16:01:27 +01:00
|
|
|
end, exmpp_xml:get_child_elements(Packet)),
|
|
|
|
exmpp_xml:set_children(Packet, FEls).
|
2003-03-23 21:08:44 +01:00
|
|
|
|
2008-12-01 16:01:27 +01:00
|
|
|
strip_status(#xmlel{name = 'presence', children = Children} = Packet) ->
|
2008-09-16 16:39:57 +02:00
|
|
|
FEls = lists:filter(
|
2008-12-01 16:01:27 +01:00
|
|
|
fun(#xmlel{name = 'status'}) ->
|
2008-09-16 16:39:57 +02:00
|
|
|
false;
|
|
|
|
(_) -> true
|
2008-12-01 16:01:27 +01:00
|
|
|
end, Children),
|
|
|
|
exmpp_xml:set_children(Packet,FEls).
|
2003-03-23 21:08:44 +01:00
|
|
|
|
|
|
|
add_user_presence(JID, Presence, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_jid(JID),
|
2003-03-23 21:08:44 +01:00
|
|
|
FPresence = filter_presence(Presence),
|
|
|
|
Users =
|
|
|
|
?DICT:update(
|
|
|
|
LJID,
|
|
|
|
fun(#user{} = User) ->
|
|
|
|
User#user{last_presence = FPresence}
|
|
|
|
end, StateData#state.users),
|
|
|
|
StateData#state{users = Users}.
|
|
|
|
|
|
|
|
add_user_presence_un(JID, Presence, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_jid(JID),
|
2003-03-23 21:08:44 +01:00
|
|
|
FPresence = filter_presence(Presence),
|
|
|
|
Users =
|
|
|
|
?DICT:update(
|
|
|
|
LJID,
|
|
|
|
fun(#user{} = User) ->
|
|
|
|
User#user{last_presence = FPresence,
|
|
|
|
role = none}
|
|
|
|
end, StateData#state.users),
|
|
|
|
StateData#state{users = Users}.
|
|
|
|
|
|
|
|
|
|
|
|
is_nick_exists(Nick, StateData) ->
|
|
|
|
?DICT:fold(fun(_, #user{nick = N}, B) ->
|
|
|
|
B orelse (N == Nick)
|
|
|
|
end, false, StateData#state.users).
|
|
|
|
|
|
|
|
find_jid_by_nick(Nick, StateData) ->
|
|
|
|
?DICT:fold(fun(_, #user{jid = JID, nick = N}, R) ->
|
|
|
|
case Nick of
|
|
|
|
N -> JID;
|
|
|
|
_ -> R
|
|
|
|
end
|
|
|
|
end, false, StateData#state.users).
|
|
|
|
|
|
|
|
is_nick_change(JID, Nick, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_jid(JID),
|
2003-03-23 21:08:44 +01:00
|
|
|
case Nick of
|
2009-01-15 20:41:06 +01:00
|
|
|
<<>> ->
|
2003-03-23 21:08:44 +01:00
|
|
|
false;
|
|
|
|
_ ->
|
|
|
|
{ok, #user{nick = OldNick}} =
|
|
|
|
?DICT:find(LJID, StateData#state.users),
|
|
|
|
Nick /= OldNick
|
|
|
|
end.
|
|
|
|
|
2008-12-01 16:01:27 +01:00
|
|
|
add_new_user(From, Nick, Packet, StateData) ->
|
|
|
|
Lang = exmpp_stanza:get_lang(Packet),
|
2007-08-29 19:54:45 +02:00
|
|
|
MaxUsers = get_max_users(StateData),
|
|
|
|
MaxAdminUsers = MaxUsers + get_max_users_admin_threshold(StateData),
|
|
|
|
NUsers = dict:fold(fun(_, _, Acc) -> Acc + 1 end, 0,
|
|
|
|
StateData#state.users),
|
2007-05-16 12:13:04 +02:00
|
|
|
Affiliation = get_affiliation(From, StateData),
|
2007-08-29 19:54:45 +02:00
|
|
|
ServiceAffiliation = get_service_affiliation(From, StateData),
|
2007-12-03 11:47:42 +01:00
|
|
|
NConferences = tab_count_user(From),
|
|
|
|
MaxConferences = gen_mod:get_module_opt(
|
|
|
|
StateData#state.server_host,
|
|
|
|
mod_muc, max_user_conferences, 10),
|
|
|
|
case {(ServiceAffiliation == owner orelse
|
|
|
|
MaxUsers == none orelse
|
|
|
|
((Affiliation == admin orelse Affiliation == owner) andalso
|
|
|
|
NUsers < MaxAdminUsers) orelse
|
|
|
|
NUsers < MaxUsers) andalso
|
|
|
|
NConferences < MaxConferences,
|
2007-08-28 16:35:50 +02:00
|
|
|
is_nick_exists(Nick, StateData),
|
2009-01-15 20:41:06 +01:00
|
|
|
mod_muc:can_use_nick(StateData#state.host, From, Nick),
|
2007-05-16 12:13:04 +02:00
|
|
|
get_default_role(Affiliation, StateData)} of
|
2007-08-28 16:35:50 +02:00
|
|
|
{false, _, _, _} ->
|
|
|
|
% max user reached and user is not admin or owner
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(
|
2007-08-28 16:35:50 +02:00
|
|
|
Packet,
|
2008-12-01 16:01:27 +01:00
|
|
|
'service-unavailable'),
|
2007-08-28 16:35:50 +02:00
|
|
|
ejabberd_router:route( % TODO: s/Nick/""/
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(StateData#state.jid, Nick),
|
2007-08-28 16:35:50 +02:00
|
|
|
From, Err),
|
|
|
|
StateData;
|
|
|
|
{_, _, _, none} ->
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(
|
2007-05-16 12:13:04 +02:00
|
|
|
Packet,
|
|
|
|
case Affiliation of
|
|
|
|
outcast ->
|
|
|
|
ErrText = "You have been banned from this room",
|
2008-12-01 16:01:27 +01:00
|
|
|
exmpp_stanza:error(Packet#xmlel.ns,
|
|
|
|
'forbidden',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)});
|
2007-05-16 12:13:04 +02:00
|
|
|
_ ->
|
2009-04-08 21:04:13 +02:00
|
|
|
ErrText = "Membership is required to enter this room",
|
2008-12-01 16:01:27 +01:00
|
|
|
exmpp_stanza:error(Packet#xmlel.ns,
|
|
|
|
'registration-required',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})
|
2007-05-16 12:13:04 +02:00
|
|
|
end),
|
|
|
|
ejabberd_router:route( % TODO: s/Nick/""/
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(StateData#state.jid, Nick),
|
2007-05-16 12:13:04 +02:00
|
|
|
From, Err),
|
|
|
|
StateData;
|
2007-08-28 16:35:50 +02:00
|
|
|
{_, true, _, _} ->
|
2009-04-08 21:04:13 +02:00
|
|
|
ErrText = "That nickname is already in use by another occupant",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
exmpp_stanza:error(Packet#xmlel.ns,
|
|
|
|
'conflict',
|
|
|
|
{Lang, translate:translate(Lang, ErrText)})),
|
2004-03-02 22:16:55 +01:00
|
|
|
ejabberd_router:route(
|
|
|
|
% TODO: s/Nick/""/
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(StateData#state.jid, Nick),
|
2004-03-02 22:16:55 +01:00
|
|
|
From, Err),
|
|
|
|
StateData;
|
2007-08-28 16:35:50 +02:00
|
|
|
{_, _, false, _} ->
|
2009-04-08 21:04:13 +02:00
|
|
|
ErrText = "That nickname is registered by another person",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(Packet,
|
|
|
|
?ERR(Packet, 'conflict', Lang, ErrText)),
|
2003-08-15 21:17:12 +02:00
|
|
|
ejabberd_router:route(
|
2003-10-07 22:31:44 +02:00
|
|
|
% TODO: s/Nick/""/
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(StateData#state.jid, Nick),
|
2003-08-15 21:17:12 +02:00
|
|
|
From, Err),
|
|
|
|
StateData;
|
2007-08-28 16:35:50 +02:00
|
|
|
{_, _, _, Role} ->
|
2009-05-26 19:20:09 +02:00
|
|
|
case check_password(ServiceAffiliation, Affiliation,
|
|
|
|
exmpp_xml:get_child_elements(Packet), From,
|
2008-12-01 16:01:27 +01:00
|
|
|
StateData) of
|
2007-05-16 12:13:04 +02:00
|
|
|
true ->
|
|
|
|
NewState =
|
|
|
|
add_user_presence(
|
|
|
|
From, Packet,
|
|
|
|
add_online_user(From, Nick, Role, StateData)),
|
|
|
|
if not (NewState#state.config)#config.anonymous ->
|
2008-12-01 16:01:27 +01:00
|
|
|
WPacket =
|
|
|
|
#xmlel{name = 'message',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('type', <<"groupchat">>)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [
|
|
|
|
#xmlel{name = 'body',
|
|
|
|
children = [#xmlcdata{cdata =
|
|
|
|
translate:translate(Lang,
|
|
|
|
"This room is not anonymous")}]}]},
|
2007-05-16 12:13:04 +02:00
|
|
|
ejabberd_router:route(
|
|
|
|
StateData#state.jid,
|
|
|
|
From, WPacket);
|
|
|
|
true ->
|
|
|
|
ok
|
|
|
|
end,
|
|
|
|
send_existing_presences(From, NewState),
|
|
|
|
send_new_presence(From, NewState),
|
2008-12-01 16:01:27 +01:00
|
|
|
Shift = count_stanza_shift(Nick,
|
|
|
|
exmpp_xml:get_child_elements(Packet),
|
|
|
|
NewState),
|
2007-05-16 12:13:04 +02:00
|
|
|
case send_history(From, Shift, NewState) of
|
|
|
|
true ->
|
|
|
|
ok;
|
|
|
|
_ ->
|
|
|
|
send_subject(From, Lang, StateData)
|
|
|
|
end,
|
|
|
|
case NewState#state.just_created of
|
|
|
|
true ->
|
|
|
|
NewState#state{just_created = false};
|
|
|
|
false ->
|
2009-05-26 19:20:09 +02:00
|
|
|
Robots = ?DICT:erase(From, StateData#state.robots),
|
|
|
|
NewState#state{robots = Robots}
|
2007-05-16 12:13:04 +02:00
|
|
|
end;
|
|
|
|
nopass ->
|
2009-08-19 19:59:49 +02:00
|
|
|
ErrText = "A password is required to enter this room",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(
|
|
|
|
Packet, ?ERR(Packet, 'not-authorized', Lang, ErrText)),
|
2003-08-15 21:17:12 +02:00
|
|
|
ejabberd_router:route( % TODO: s/Nick/""/
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(
|
2007-05-16 12:13:04 +02:00
|
|
|
StateData#state.jid, Nick),
|
2003-08-15 21:17:12 +02:00
|
|
|
From, Err),
|
|
|
|
StateData;
|
2009-05-26 19:20:09 +02:00
|
|
|
captcha_required ->
|
|
|
|
ID = randoms:get_string(),
|
|
|
|
SID = case exmpp_stanza:get_id(Packet) of undefined -> ""; SID1 -> SID1 end,
|
|
|
|
RoomJID = StateData#state.jid,
|
|
|
|
To = jid_replace_resource(RoomJID, Nick),
|
|
|
|
case ejabberd_captcha:create_captcha(
|
|
|
|
ID, SID, RoomJID, To, Lang, From) of
|
|
|
|
{ok, CaptchaEls} ->
|
|
|
|
MsgPkt = {xmlelement, "message", [{"id", ID}], CaptchaEls},
|
|
|
|
Robots = ?DICT:store(From,
|
|
|
|
{Nick, Packet}, StateData#state.robots),
|
|
|
|
ejabberd_router:route(RoomJID, From, MsgPkt),
|
|
|
|
StateData#state{robots = Robots};
|
|
|
|
error ->
|
|
|
|
ErrText = "Unable to generate a captcha",
|
|
|
|
Err = exmpp_stanza:reply_with_error(
|
|
|
|
Packet, ?ERR(Packet, 'internal-server-error', Lang, ErrText)),
|
|
|
|
ejabberd_router:route( % TODO: s/Nick/""/
|
|
|
|
jid_replace_resource(
|
|
|
|
StateData#state.jid, Nick),
|
|
|
|
From, Err),
|
|
|
|
StateData
|
|
|
|
end;
|
2003-08-15 21:17:12 +02:00
|
|
|
_ ->
|
2007-05-16 12:13:04 +02:00
|
|
|
ErrText = "Incorrect password",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(
|
|
|
|
Packet, ?ERR(Packet, 'not-authorized', Lang, ErrText)),
|
2007-05-16 12:13:04 +02:00
|
|
|
ejabberd_router:route( % TODO: s/Nick/""/
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(
|
2007-05-16 12:13:04 +02:00
|
|
|
StateData#state.jid, Nick),
|
|
|
|
From, Err),
|
|
|
|
StateData
|
|
|
|
end
|
2003-08-15 21:17:12 +02:00
|
|
|
end.
|
|
|
|
|
2009-05-26 19:20:09 +02:00
|
|
|
check_password(owner, _Affiliation, _Els, _From, _StateData) ->
|
2009-03-03 20:07:24 +01:00
|
|
|
%% Don't check pass if user is owner in MUC service (access_admin option)
|
2003-08-15 21:17:12 +02:00
|
|
|
true;
|
2009-05-26 19:20:09 +02:00
|
|
|
check_password(_ServiceAffiliation, Affiliation, Els, From, StateData) ->
|
2003-08-15 21:17:12 +02:00
|
|
|
case (StateData#state.config)#config.password_protected of
|
|
|
|
false ->
|
2009-05-26 19:20:09 +02:00
|
|
|
check_captcha(Affiliation, From, StateData);
|
2003-08-15 21:17:12 +02:00
|
|
|
true ->
|
|
|
|
Pass = extract_password(Els),
|
2004-03-02 22:16:55 +01:00
|
|
|
case Pass of
|
|
|
|
false ->
|
|
|
|
nopass;
|
2003-08-15 21:17:12 +02:00
|
|
|
_ ->
|
2004-03-02 22:16:55 +01:00
|
|
|
case (StateData#state.config)#config.password of
|
|
|
|
Pass ->
|
|
|
|
true;
|
|
|
|
_ ->
|
2009-05-26 19:20:09 +02:00
|
|
|
false
|
2004-03-02 22:16:55 +01:00
|
|
|
end
|
2003-08-15 21:17:12 +02:00
|
|
|
end
|
|
|
|
end.
|
|
|
|
|
2009-05-26 19:20:09 +02:00
|
|
|
check_captcha(Affiliation, From, StateData) ->
|
|
|
|
case (StateData#state.config)#config.captcha_protected
|
|
|
|
andalso ejabberd_captcha:is_feature_available() of
|
|
|
|
true when Affiliation == none ->
|
|
|
|
case ?DICT:find(From, StateData#state.robots) of
|
|
|
|
{ok, passed} ->
|
|
|
|
true;
|
|
|
|
_ ->
|
|
|
|
captcha_required
|
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
true
|
|
|
|
end.
|
|
|
|
|
2003-08-15 21:17:12 +02:00
|
|
|
extract_password([]) ->
|
2004-03-02 22:16:55 +01:00
|
|
|
false;
|
2008-12-01 16:01:27 +01:00
|
|
|
extract_password([#xmlel{ns = XMLNS} = El | Els]) ->
|
|
|
|
case XMLNS of
|
2003-08-15 21:17:12 +02:00
|
|
|
?NS_MUC ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case exmpp_xml:get_element(El, 'password') of
|
|
|
|
undefined ->
|
2004-03-02 22:16:55 +01:00
|
|
|
false;
|
|
|
|
SubEl ->
|
2008-12-01 16:01:27 +01:00
|
|
|
exmpp_xml:get_cdata(SubEl)
|
2004-03-02 22:16:55 +01:00
|
|
|
end;
|
2003-08-15 21:17:12 +02:00
|
|
|
_ ->
|
|
|
|
extract_password(Els)
|
|
|
|
end;
|
|
|
|
extract_password([_ | Els]) ->
|
|
|
|
extract_password(Els).
|
|
|
|
|
2004-02-15 21:10:40 +01:00
|
|
|
count_stanza_shift(Nick, Els, StateData) ->
|
|
|
|
HL = lqueue_to_list(StateData#state.history),
|
|
|
|
Since = extract_history(Els, "since"),
|
|
|
|
Shift0 = case Since of
|
|
|
|
false ->
|
|
|
|
0;
|
|
|
|
_ ->
|
|
|
|
Sin = calendar:datetime_to_gregorian_seconds(Since),
|
|
|
|
count_seconds_shift(Sin, HL)
|
|
|
|
end,
|
|
|
|
Seconds = extract_history(Els, "seconds"),
|
|
|
|
Shift1 = case Seconds of
|
|
|
|
false ->
|
|
|
|
0;
|
|
|
|
_ ->
|
|
|
|
Sec = calendar:datetime_to_gregorian_seconds(
|
|
|
|
calendar:now_to_universal_time(now())) - Seconds,
|
|
|
|
count_seconds_shift(Sec, HL)
|
|
|
|
end,
|
|
|
|
MaxStanzas = extract_history(Els, "maxstanzas"),
|
|
|
|
Shift2 = case MaxStanzas of
|
|
|
|
false ->
|
|
|
|
0;
|
|
|
|
_ ->
|
|
|
|
count_maxstanzas_shift(MaxStanzas, HL)
|
|
|
|
end,
|
|
|
|
MaxChars = extract_history(Els, "maxchars"),
|
|
|
|
Shift3 = case MaxChars of
|
|
|
|
false ->
|
|
|
|
0;
|
|
|
|
_ ->
|
|
|
|
count_maxchars_shift(Nick, MaxChars, HL)
|
|
|
|
end,
|
|
|
|
lists:max([Shift0, Shift1, Shift2, Shift3]).
|
|
|
|
|
|
|
|
count_seconds_shift(Seconds, HistoryList) ->
|
|
|
|
lists:sum(
|
|
|
|
lists:map(
|
|
|
|
fun({_Nick, _Packet, _HaveSubject, TimeStamp, _Size}) ->
|
|
|
|
T = calendar:datetime_to_gregorian_seconds(TimeStamp),
|
|
|
|
if
|
|
|
|
T < Seconds ->
|
|
|
|
1;
|
|
|
|
true ->
|
|
|
|
0
|
|
|
|
end
|
|
|
|
end, HistoryList)).
|
2003-08-15 21:17:12 +02:00
|
|
|
|
2004-02-15 21:10:40 +01:00
|
|
|
count_maxstanzas_shift(MaxStanzas, HistoryList) ->
|
|
|
|
S = length(HistoryList) - MaxStanzas,
|
|
|
|
if
|
|
|
|
S =< 0 ->
|
|
|
|
0;
|
|
|
|
true ->
|
|
|
|
S
|
|
|
|
end.
|
|
|
|
|
|
|
|
count_maxchars_shift(Nick, MaxSize, HistoryList) ->
|
|
|
|
NLen = string:len(Nick) + 1,
|
|
|
|
Sizes = lists:map(
|
|
|
|
fun({_Nick, _Packet, _HaveSubject, _TimeStamp, Size}) ->
|
|
|
|
Size + NLen
|
|
|
|
end, HistoryList),
|
|
|
|
calc_shift(MaxSize, Sizes).
|
|
|
|
|
|
|
|
calc_shift(MaxSize, Sizes) ->
|
|
|
|
Total = lists:sum(Sizes),
|
|
|
|
calc_shift(MaxSize, Total, 0, Sizes).
|
|
|
|
|
|
|
|
calc_shift(_MaxSize, _Size, Shift, []) ->
|
|
|
|
Shift;
|
|
|
|
calc_shift(MaxSize, Size, Shift, [S | TSizes]) ->
|
|
|
|
if
|
|
|
|
MaxSize >= Size ->
|
|
|
|
Shift;
|
|
|
|
true ->
|
|
|
|
calc_shift(MaxSize, Size - S, Shift + 1, TSizes)
|
|
|
|
end.
|
|
|
|
|
2004-05-17 22:36:41 +02:00
|
|
|
extract_history([], _Type) ->
|
2004-02-15 21:10:40 +01:00
|
|
|
false;
|
2008-12-01 16:01:27 +01:00
|
|
|
extract_history([#xmlel{ns = XMLNS} = El | Els], Type) ->
|
|
|
|
case XMLNS of
|
2004-02-15 21:10:40 +01:00
|
|
|
?NS_MUC ->
|
2008-12-01 16:01:27 +01:00
|
|
|
AttrVal = exmpp_xml:get_path(El,
|
|
|
|
[{element, 'history'}, {attribute, Type,""}]),
|
2004-02-15 21:10:40 +01:00
|
|
|
case Type of
|
|
|
|
"since" ->
|
2004-09-17 21:52:59 +02:00
|
|
|
case jlib:datetime_string_to_timestamp(AttrVal) of
|
|
|
|
undefined ->
|
2004-03-02 22:16:55 +01:00
|
|
|
false;
|
2004-09-17 21:52:59 +02:00
|
|
|
TS ->
|
|
|
|
calendar:now_to_universal_time(TS)
|
2004-03-02 22:16:55 +01:00
|
|
|
end;
|
2004-02-15 21:10:40 +01:00
|
|
|
_ ->
|
|
|
|
case catch list_to_integer(AttrVal) of
|
2004-12-05 21:54:55 +01:00
|
|
|
IntVal when is_integer(IntVal) and (IntVal >= 0) ->
|
2004-09-17 21:52:59 +02:00
|
|
|
IntVal;
|
|
|
|
_ ->
|
|
|
|
false
|
2004-02-15 21:10:40 +01:00
|
|
|
end
|
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
extract_history(Els, Type)
|
|
|
|
end;
|
|
|
|
extract_history([_ | Els], Type) ->
|
|
|
|
extract_history(Els, Type).
|
|
|
|
|
2003-08-15 21:17:12 +02:00
|
|
|
|
2003-04-13 21:22:46 +02:00
|
|
|
send_update_presence(JID, StateData) ->
|
2009-01-15 20:41:06 +01:00
|
|
|
send_update_presence(JID, <<>>, StateData).
|
2008-09-16 16:39:57 +02:00
|
|
|
|
|
|
|
send_update_presence(JID, Reason, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_jid(JID),
|
2003-04-13 21:22:46 +02:00
|
|
|
LJIDs = case LJID of
|
2008-12-01 16:01:27 +01:00
|
|
|
{U, S, undefined} ->
|
2003-04-13 21:22:46 +02:00
|
|
|
?DICT:fold(
|
|
|
|
fun(J, _, Js) ->
|
|
|
|
case J of
|
|
|
|
{U, S, _} ->
|
|
|
|
[J | Js];
|
|
|
|
_ ->
|
|
|
|
Js
|
|
|
|
end
|
|
|
|
end, [], StateData#state.users);
|
|
|
|
_ ->
|
|
|
|
case ?DICT:is_key(LJID, StateData#state.users) of
|
|
|
|
true ->
|
|
|
|
[LJID];
|
|
|
|
_ ->
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
lists:foreach(fun(J) ->
|
2008-09-16 16:39:57 +02:00
|
|
|
send_new_presence(J, Reason, StateData)
|
2003-04-13 21:22:46 +02:00
|
|
|
end, LJIDs).
|
2003-03-23 21:08:44 +01:00
|
|
|
|
|
|
|
send_new_presence(NJID, StateData) ->
|
2009-01-15 20:41:06 +01:00
|
|
|
send_new_presence(NJID, <<>>, StateData).
|
2008-09-16 16:39:57 +02:00
|
|
|
|
|
|
|
send_new_presence(NJID, Reason, StateData) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
{ok, #user{jid = RealJID,
|
|
|
|
nick = Nick,
|
|
|
|
role = Role,
|
|
|
|
last_presence = Presence}} =
|
2008-12-01 16:01:27 +01:00
|
|
|
?DICT:find(jlib:short_prepd_jid(NJID), StateData#state.users),
|
2003-03-23 21:08:44 +01:00
|
|
|
Affiliation = get_affiliation(NJID, StateData),
|
2009-01-09 20:18:46 +01:00
|
|
|
SAffiliation = affiliation_to_binary(Affiliation),
|
|
|
|
SRole = role_to_binary(Role),
|
2003-03-23 21:08:44 +01:00
|
|
|
lists:foreach(
|
2004-05-17 22:36:41 +02:00
|
|
|
fun({_LJID, Info}) ->
|
2003-03-26 21:51:18 +01:00
|
|
|
ItemAttrs =
|
|
|
|
case (Info#user.role == moderator) orelse
|
|
|
|
((StateData#state.config)#config.anonymous == false) of
|
|
|
|
true ->
|
2009-06-01 18:59:08 +02:00
|
|
|
[?XMLATTR('jid', exmpp_jid:to_binary(RealJID)),
|
2009-01-21 14:34:26 +01:00
|
|
|
?XMLATTR('affiliation', SAffiliation),
|
|
|
|
?XMLATTR('role', SRole)];
|
2003-03-26 21:51:18 +01:00
|
|
|
_ ->
|
2009-01-21 14:34:26 +01:00
|
|
|
[?XMLATTR('affiliation', SAffiliation),
|
|
|
|
?XMLATTR('role', SRole)]
|
2003-03-26 21:51:18 +01:00
|
|
|
end,
|
2008-09-16 16:39:57 +02:00
|
|
|
ItemEls = case Reason of
|
2009-01-15 20:41:06 +01:00
|
|
|
<<>> ->
|
2008-09-16 16:39:57 +02:00
|
|
|
[];
|
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
[#xmlel{name = 'reason',
|
|
|
|
children = [#xmlcdata{cdata = Reason}]}]
|
2008-09-16 16:39:57 +02:00
|
|
|
end,
|
2004-05-01 22:10:25 +02:00
|
|
|
Status = case StateData#state.just_created of
|
|
|
|
true ->
|
2008-12-01 16:01:27 +01:00
|
|
|
[#xmlel{name = 'status',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('code', <<"201">>)]}];
|
2004-05-01 22:10:25 +02:00
|
|
|
false ->
|
|
|
|
[]
|
|
|
|
end,
|
2008-12-01 16:01:27 +01:00
|
|
|
Packet = exmpp_xml:append_child(Presence,
|
|
|
|
#xmlel{ns = ?NS_MUC_USER, name = 'x',
|
|
|
|
children = [#xmlel{ns = ?NS_MUC_USER, name ='item',
|
|
|
|
attrs = ItemAttrs,
|
|
|
|
children = ItemEls} | Status]}),
|
2003-03-23 21:08:44 +01:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(StateData#state.jid, Nick),
|
2003-03-23 21:08:44 +01:00
|
|
|
Info#user.jid,
|
|
|
|
Packet)
|
|
|
|
end, ?DICT:to_list(StateData#state.users)).
|
|
|
|
|
|
|
|
|
|
|
|
send_existing_presences(ToJID, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LToJID = jlib:short_prepd_jid(ToJID),
|
2003-03-23 21:08:44 +01:00
|
|
|
{ok, #user{jid = RealToJID,
|
|
|
|
role = Role}} =
|
|
|
|
?DICT:find(LToJID, StateData#state.users),
|
|
|
|
lists:foreach(
|
|
|
|
fun({LJID, #user{jid = FromJID,
|
|
|
|
nick = FromNick,
|
|
|
|
role = FromRole,
|
|
|
|
last_presence = Presence
|
|
|
|
}}) ->
|
|
|
|
case RealToJID of
|
|
|
|
FromJID ->
|
|
|
|
ok;
|
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{N,D,R} = LJID,
|
2009-06-01 18:26:00 +02:00
|
|
|
FromAffiliation = get_affiliation(exmpp_jid:make(N,D,R),
|
2008-12-01 16:01:27 +01:00
|
|
|
StateData),
|
2003-03-26 21:51:18 +01:00
|
|
|
ItemAttrs =
|
|
|
|
case (Role == moderator) orelse
|
|
|
|
((StateData#state.config)#config.anonymous ==
|
|
|
|
false) of
|
|
|
|
true ->
|
2009-06-01 18:59:08 +02:00
|
|
|
[?XMLATTR('jid', exmpp_jid:to_binary(FromJID)),
|
2009-01-21 14:34:26 +01:00
|
|
|
?XMLATTR('affiliation',
|
|
|
|
affiliation_to_binary(FromAffiliation)),
|
|
|
|
?XMLATTR('role', role_to_binary(FromRole))];
|
2003-03-26 21:51:18 +01:00
|
|
|
_ ->
|
2009-01-21 14:34:26 +01:00
|
|
|
[?XMLATTR('affiliation',
|
|
|
|
affiliation_to_binary(FromAffiliation)),
|
|
|
|
?XMLATTR('role', role_to_binary(FromRole))]
|
2003-03-26 21:51:18 +01:00
|
|
|
end,
|
2008-12-01 16:01:27 +01:00
|
|
|
Packet = exmpp_xml:append_child(Presence,
|
|
|
|
#xmlel{ns = ?NS_MUC_USER, name = 'x',
|
|
|
|
children = [#xmlel{ns = ?NS_MUC_USER, name ='item',
|
|
|
|
attrs = ItemAttrs}]}),
|
2003-03-23 21:08:44 +01:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(
|
2003-10-07 22:31:44 +02:00
|
|
|
StateData#state.jid, FromNick),
|
2003-03-23 21:08:44 +01:00
|
|
|
RealToJID,
|
|
|
|
Packet)
|
|
|
|
end
|
|
|
|
end, ?DICT:to_list(StateData#state.users)).
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-06-25 18:43:42 +02:00
|
|
|
now_to_usec({MSec, Sec, USec}) ->
|
|
|
|
(MSec*1000000 + Sec)*1000000 + USec.
|
|
|
|
|
|
|
|
|
2003-03-23 21:08:44 +01:00
|
|
|
change_nick(JID, Nick, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_jid(JID),
|
2003-03-23 21:08:44 +01:00
|
|
|
{ok, #user{nick = OldNick}} =
|
|
|
|
?DICT:find(LJID, StateData#state.users),
|
|
|
|
Users =
|
|
|
|
?DICT:update(
|
|
|
|
LJID,
|
|
|
|
fun(#user{} = User) ->
|
|
|
|
User#user{nick = Nick}
|
|
|
|
end, StateData#state.users),
|
|
|
|
NewStateData = StateData#state{users = Users},
|
|
|
|
send_nick_changing(JID, OldNick, NewStateData),
|
2006-03-14 05:26:15 +01:00
|
|
|
add_to_log(nickchange, {OldNick, Nick}, StateData),
|
2003-03-23 21:08:44 +01:00
|
|
|
NewStateData.
|
|
|
|
|
|
|
|
send_nick_changing(JID, OldNick, StateData) ->
|
|
|
|
{ok, #user{jid = RealJID,
|
|
|
|
nick = Nick,
|
|
|
|
role = Role,
|
|
|
|
last_presence = Presence}} =
|
2008-12-01 16:01:27 +01:00
|
|
|
?DICT:find(jlib:short_prepd_jid(JID), StateData#state.users),
|
2003-03-23 21:08:44 +01:00
|
|
|
Affiliation = get_affiliation(JID, StateData),
|
2009-01-09 20:18:46 +01:00
|
|
|
SAffiliation = affiliation_to_binary(Affiliation),
|
|
|
|
SRole = role_to_binary(Role),
|
2003-03-23 21:08:44 +01:00
|
|
|
lists:foreach(
|
2004-05-17 22:36:41 +02:00
|
|
|
fun({_LJID, Info}) ->
|
2003-03-26 21:51:18 +01:00
|
|
|
ItemAttrs1 =
|
|
|
|
case (Info#user.role == moderator) orelse
|
|
|
|
((StateData#state.config)#config.anonymous == false) of
|
|
|
|
true ->
|
2009-06-01 18:59:08 +02:00
|
|
|
[?XMLATTR('jid', exmpp_jid:to_binary(RealJID)),
|
2009-01-21 14:34:26 +01:00
|
|
|
?XMLATTR('affiliation', SAffiliation),
|
|
|
|
?XMLATTR('role', SRole),
|
|
|
|
?XMLATTR('nick', Nick)];
|
2003-03-26 21:51:18 +01:00
|
|
|
_ ->
|
2009-01-21 14:34:26 +01:00
|
|
|
[?XMLATTR('affiliation', SAffiliation),
|
|
|
|
?XMLATTR('role', SRole),
|
|
|
|
?XMLATTR('nick', Nick)]
|
2003-03-26 21:51:18 +01:00
|
|
|
end,
|
|
|
|
ItemAttrs2 =
|
|
|
|
case (Info#user.role == moderator) orelse
|
|
|
|
((StateData#state.config)#config.anonymous == false) of
|
|
|
|
true ->
|
2009-06-01 18:59:08 +02:00
|
|
|
[?XMLATTR('jid', exmpp_jid:to_binary(RealJID)),
|
2009-01-21 14:34:26 +01:00
|
|
|
?XMLATTR('affiliation', SAffiliation),
|
|
|
|
?XMLATTR('role', SRole)];
|
2003-03-26 21:51:18 +01:00
|
|
|
_ ->
|
2009-01-21 14:34:26 +01:00
|
|
|
[?XMLATTR('affiliation', SAffiliation),
|
|
|
|
?XMLATTR('role', SRole)]
|
2003-03-26 21:51:18 +01:00
|
|
|
end,
|
2003-03-23 21:08:44 +01:00
|
|
|
Packet1 =
|
2008-12-01 16:01:27 +01:00
|
|
|
#xmlel{ns = ?NS_JABBER_CLIENT,
|
|
|
|
name = 'presence',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('type', <<"unavailable">>)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{ns = ?NS_MUC_USER, name = 'x',
|
|
|
|
children = [
|
|
|
|
#xmlel{ns = ?NS_MUC_USER, name = 'item',
|
|
|
|
attrs = ItemAttrs1},
|
|
|
|
#xmlel{ns = ?NS_MUC_USER, name = 'status',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('code',
|
|
|
|
<<"303">>)]}]}]},
|
2008-12-01 16:01:27 +01:00
|
|
|
|
|
|
|
Packet2 = exmpp_xml:append_child(
|
2003-03-23 21:08:44 +01:00
|
|
|
Presence,
|
2008-12-01 16:01:27 +01:00
|
|
|
#xmlel{ns = ?NS_MUC_USER, name = 'x',
|
|
|
|
children =[#xmlel{ns = ?NS_MUC_USER,
|
|
|
|
name = 'item',
|
|
|
|
attrs = ItemAttrs2}]}),
|
|
|
|
|
2003-03-23 21:08:44 +01:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(StateData#state.jid, OldNick),
|
2003-03-23 21:08:44 +01:00
|
|
|
Info#user.jid,
|
|
|
|
Packet1),
|
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(StateData#state.jid, Nick),
|
2003-03-23 21:08:44 +01:00
|
|
|
Info#user.jid,
|
|
|
|
Packet2)
|
|
|
|
end, ?DICT:to_list(StateData#state.users)).
|
|
|
|
|
|
|
|
|
|
|
|
lqueue_new(Max) ->
|
|
|
|
#lqueue{queue = queue:new(),
|
|
|
|
len = 0,
|
|
|
|
max = Max}.
|
|
|
|
|
2006-09-05 17:53:54 +02:00
|
|
|
%% If the message queue limit is set to 0, do not store messages.
|
2006-09-10 01:33:00 +02:00
|
|
|
lqueue_in(_Item, LQ = #lqueue{max = 0}) ->
|
2006-09-05 17:53:54 +02:00
|
|
|
LQ;
|
|
|
|
%% Otherwise, rotate messages in the queue store.
|
2006-09-10 01:33:00 +02:00
|
|
|
lqueue_in(Item, #lqueue{queue = Q1, len = Len, max = Max}) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
Q2 = queue:in(Item, Q1),
|
|
|
|
if
|
|
|
|
Len >= Max ->
|
|
|
|
Q3 = lqueue_cut(Q2, Len - Max + 1),
|
|
|
|
#lqueue{queue = Q3, len = Max, max = Max};
|
|
|
|
true ->
|
|
|
|
#lqueue{queue = Q2, len = Len + 1, max = Max}
|
|
|
|
end.
|
|
|
|
|
|
|
|
lqueue_cut(Q, 0) ->
|
|
|
|
Q;
|
|
|
|
lqueue_cut(Q, N) ->
|
|
|
|
{_, Q1} = queue:out(Q),
|
|
|
|
lqueue_cut(Q1, N - 1).
|
|
|
|
|
|
|
|
lqueue_to_list(#lqueue{queue = Q1}) ->
|
|
|
|
queue:to_list(Q1).
|
|
|
|
|
|
|
|
|
2009-06-30 18:55:26 +02:00
|
|
|
add_message_to_history(FromNick, FromJID, Packet, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
HaveSubject = exmpp_xml:has_element(Packet, 'subject'),
|
2004-02-15 21:10:40 +01:00
|
|
|
TimeStamp = calendar:now_to_universal_time(now()),
|
2009-06-30 18:55:26 +02:00
|
|
|
%% Chatroom history is stored as XMPP packets, so
|
|
|
|
%% the decision to include the original sender's JID or not is based on the
|
|
|
|
%% chatroom configuration when the message was originally sent.
|
|
|
|
%% Also, if the chatroom is anonymous, even moderators will not get the real JID
|
|
|
|
SenderJid = case ((StateData#state.config)#config.anonymous) of
|
|
|
|
true -> StateData#state.jid;
|
|
|
|
false -> FromJID
|
|
|
|
end,
|
|
|
|
TSPacket = exmpp_xml:append_children(Packet,
|
|
|
|
[jlib:timestamp_to_xml(TimeStamp, utc, SenderJid, ""),
|
|
|
|
%% TODO: Delete the next line once XEP-0091 is Obsolete
|
|
|
|
jlib:timestamp_to_xml(TimeStamp)]),
|
2008-12-01 16:01:27 +01:00
|
|
|
SPacket = exmpp_stanza:set_recipient(
|
|
|
|
exmpp_stanza:set_sender(TSPacket,
|
|
|
|
jid_replace_resource(StateData#state.jid, FromNick)),
|
|
|
|
StateData#state.jid),
|
|
|
|
|
2009-01-15 16:21:54 +01:00
|
|
|
Size = erlang:iolist_size(exmpp_xml:document_to_iolist(SPacket)),
|
2004-02-15 21:10:40 +01:00
|
|
|
Q1 = lqueue_in({FromNick, TSPacket, HaveSubject, TimeStamp, Size},
|
|
|
|
StateData#state.history),
|
2006-03-14 05:26:15 +01:00
|
|
|
add_to_log(text, {FromNick, Packet}, StateData),
|
2003-03-23 21:08:44 +01:00
|
|
|
StateData#state{history = Q1}.
|
|
|
|
|
2004-02-15 21:10:40 +01:00
|
|
|
send_history(JID, Shift, StateData) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
lists:foldl(
|
2004-02-15 21:10:40 +01:00
|
|
|
fun({Nick, Packet, HaveSubject, _TimeStamp, _Size}, B) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(StateData#state.jid, Nick),
|
2003-03-23 21:08:44 +01:00
|
|
|
JID,
|
|
|
|
Packet),
|
|
|
|
B or HaveSubject
|
2004-02-15 21:10:40 +01:00
|
|
|
end, false, lists:nthtail(Shift, lqueue_to_list(StateData#state.history))).
|
2003-03-23 21:08:44 +01:00
|
|
|
|
|
|
|
|
2004-02-26 23:00:04 +01:00
|
|
|
send_subject(JID, Lang, StateData) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
case StateData#state.subject_author of
|
2009-01-15 20:41:06 +01:00
|
|
|
<<>> ->
|
2003-03-23 21:08:44 +01:00
|
|
|
ok;
|
|
|
|
Nick ->
|
|
|
|
Subject = StateData#state.subject,
|
2008-12-01 16:01:27 +01:00
|
|
|
Packet = exmpp_message:groupchat(Subject,
|
|
|
|
Nick ++ translate:translate(Lang,
|
|
|
|
" has set the subject to: ") ++ Subject),
|
2003-03-23 21:08:44 +01:00
|
|
|
ejabberd_router:route(
|
2003-10-07 22:31:44 +02:00
|
|
|
StateData#state.jid,
|
2003-03-23 21:08:44 +01:00
|
|
|
JID,
|
|
|
|
Packet)
|
|
|
|
end.
|
|
|
|
|
|
|
|
check_subject(Packet) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case exmpp_message:get_subject(Packet) of
|
|
|
|
undefined ->
|
2003-03-23 21:08:44 +01:00
|
|
|
false;
|
2008-12-01 16:01:27 +01:00
|
|
|
Subj ->
|
|
|
|
Subj
|
2003-03-23 21:08:44 +01:00
|
|
|
end.
|
|
|
|
|
|
|
|
can_change_subject(Role, StateData) ->
|
2003-03-26 21:51:18 +01:00
|
|
|
case (StateData#state.config)#config.allow_change_subj of
|
|
|
|
true ->
|
|
|
|
(Role == moderator) orelse (Role == participant);
|
|
|
|
_ ->
|
|
|
|
Role == moderator
|
|
|
|
end.
|
2003-03-23 21:08:44 +01:00
|
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
% Admin stuff
|
|
|
|
|
2004-02-26 23:00:04 +01:00
|
|
|
process_iq_admin(From, set, Lang, SubEl, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
#xmlel{children = Items} = SubEl,
|
2004-03-02 22:16:55 +01:00
|
|
|
process_admin_items_set(From, Items, Lang, StateData);
|
2003-03-23 21:08:44 +01:00
|
|
|
|
2004-02-26 23:00:04 +01:00
|
|
|
process_iq_admin(From, get, Lang, SubEl, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case exmpp_xml:get_element(SubEl, 'item') of
|
|
|
|
'undefined' ->
|
|
|
|
{error, 'bad-request'};
|
2003-03-23 21:08:44 +01:00
|
|
|
Item ->
|
|
|
|
FAffiliation = get_affiliation(From, StateData),
|
|
|
|
FRole = get_role(From, StateData),
|
2009-01-09 20:18:46 +01:00
|
|
|
case exmpp_xml:get_attribute_as_binary(Item, 'role', false) of
|
2003-03-23 21:08:44 +01:00
|
|
|
false ->
|
2009-01-09 20:18:46 +01:00
|
|
|
case exmpp_xml:get_attribute_as_binary(Item, 'affiliation', false) of
|
2003-03-23 21:08:44 +01:00
|
|
|
false ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'bad-request'};
|
|
|
|
StrAffiliation ->
|
2009-01-09 20:18:46 +01:00
|
|
|
case catch binary_to_affiliation(StrAffiliation) of
|
2003-03-23 21:08:44 +01:00
|
|
|
{'EXIT', _} ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'bad-request'};
|
2003-03-23 21:08:44 +01:00
|
|
|
SAffiliation ->
|
|
|
|
if
|
|
|
|
(FAffiliation == owner) or
|
|
|
|
(FAffiliation == admin) ->
|
|
|
|
Items = items_with_affiliation(
|
|
|
|
SAffiliation, StateData),
|
|
|
|
{result, Items, StateData};
|
|
|
|
true ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ErrText = "Administrator privileges required",
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, ?ERR(SubEl, 'forbidden', Lang, ErrText)}
|
2003-03-23 21:08:44 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end;
|
2008-12-01 16:01:27 +01:00
|
|
|
StrRole ->
|
2009-01-09 20:18:46 +01:00
|
|
|
case catch binary_to_role(StrRole) of
|
2003-03-23 21:08:44 +01:00
|
|
|
{'EXIT', _} ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'bad-request'};
|
2003-03-23 21:08:44 +01:00
|
|
|
SRole ->
|
|
|
|
if
|
|
|
|
FRole == moderator ->
|
|
|
|
Items = items_with_role(SRole, StateData),
|
|
|
|
{result, Items, StateData};
|
|
|
|
true ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ErrText = "Moderator privileges required",
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, ?ERR(SubEl, 'forbidden', Lang, ErrText)}
|
2003-03-23 21:08:44 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
items_with_role(SRole, StateData) ->
|
|
|
|
lists:map(
|
|
|
|
fun({_, U}) ->
|
|
|
|
user_to_item(U, StateData)
|
|
|
|
end, search_role(SRole, StateData)).
|
|
|
|
|
|
|
|
items_with_affiliation(SAffiliation, StateData) ->
|
|
|
|
lists:map(
|
2005-05-04 01:07:14 +02:00
|
|
|
fun({JID, {Affiliation, Reason}}) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{N, D, R} = JID,
|
|
|
|
#xmlel{name = 'item',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('affiliation',
|
|
|
|
affiliation_to_binary(Affiliation)),
|
|
|
|
?XMLATTR('jid',
|
2009-06-01 18:59:08 +02:00
|
|
|
exmpp_jid:to_binary(N, D, R))],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [ #xmlel{name = 'reason',
|
|
|
|
children = [#xmlcdata{cdata = Reason}]}]};
|
|
|
|
|
2005-05-04 01:07:14 +02:00
|
|
|
({JID, Affiliation}) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{N, D, R} = JID,
|
|
|
|
#xmlel{name = 'item',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('affiliation',
|
|
|
|
affiliation_to_binary(Affiliation)),
|
|
|
|
?XMLATTR('jid',
|
2009-06-01 18:59:08 +02:00
|
|
|
exmpp_jid:to_binary(N, D, R))]}
|
2003-03-23 21:08:44 +01:00
|
|
|
end, search_affiliation(SAffiliation, StateData)).
|
|
|
|
|
|
|
|
user_to_item(#user{role = Role,
|
|
|
|
nick = Nick,
|
|
|
|
jid = JID
|
|
|
|
}, StateData) ->
|
|
|
|
Affiliation = get_affiliation(JID, StateData),
|
2008-12-01 16:01:27 +01:00
|
|
|
#xmlel{name = 'item',
|
|
|
|
attrs = [
|
2009-01-21 14:34:26 +01:00
|
|
|
?XMLATTR('role', role_to_binary(Role)),
|
|
|
|
?XMLATTR('affiliation', affiliation_to_binary(Affiliation)),
|
|
|
|
?XMLATTR('nick', Nick),
|
2009-06-01 18:59:08 +02:00
|
|
|
?XMLATTR('jid', exmpp_jid:to_binary(JID))]
|
2008-12-01 16:01:27 +01:00
|
|
|
}.
|
2003-03-23 21:08:44 +01:00
|
|
|
|
|
|
|
search_role(Role, StateData) ->
|
|
|
|
lists:filter(
|
|
|
|
fun({_, #user{role = R}}) ->
|
|
|
|
Role == R
|
|
|
|
end, ?DICT:to_list(StateData#state.users)).
|
|
|
|
|
|
|
|
search_affiliation(Affiliation, StateData) ->
|
|
|
|
lists:filter(
|
|
|
|
fun({_, A}) ->
|
2005-05-04 01:07:14 +02:00
|
|
|
case A of
|
|
|
|
{A1, _Reason} ->
|
|
|
|
Affiliation == A1;
|
|
|
|
_ ->
|
|
|
|
Affiliation == A
|
|
|
|
end
|
2003-03-23 21:08:44 +01:00
|
|
|
end, ?DICT:to_list(StateData#state.affiliations)).
|
|
|
|
|
|
|
|
|
2004-03-02 22:16:55 +01:00
|
|
|
process_admin_items_set(UJID, Items, Lang, StateData) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
UAffiliation = get_affiliation(UJID, StateData),
|
|
|
|
URole = get_role(UJID, StateData),
|
2004-03-02 22:16:55 +01:00
|
|
|
case find_changed_items(UJID, UAffiliation, URole, Items, Lang, StateData, []) of
|
2003-03-23 21:08:44 +01:00
|
|
|
{result, Res} ->
|
2007-12-23 13:28:44 +01:00
|
|
|
?INFO_MSG("Processing MUC admin query from ~s in room ~s:~n ~p",
|
2009-06-01 18:59:08 +02:00
|
|
|
[exmpp_jid:to_binary(UJID), exmpp_jid:to_binary(StateData#state.jid), Res]),
|
2003-03-23 21:08:44 +01:00
|
|
|
NSD =
|
|
|
|
lists:foldl(
|
|
|
|
fun(E, SD) ->
|
|
|
|
case catch (
|
|
|
|
case E of
|
2009-01-03 16:15:38 +01:00
|
|
|
{JID, affiliation, owner, _} ->
|
2009-06-01 18:39:36 +02:00
|
|
|
case exmpp_jid:prep_node(JID) of
|
2009-01-03 16:15:38 +01:00
|
|
|
<<>> ->
|
|
|
|
SD;
|
|
|
|
%% TODO: <<>> or 'undefined' ?
|
|
|
|
%% TODO: double case on the E var, because
|
2009-06-01 23:04:16 +02:00
|
|
|
%% exmpp_jid:prep_node/1 can't be used in guards
|
2007-12-23 13:28:44 +01:00
|
|
|
%% If the provided JID does not have username,
|
|
|
|
%% forget the affiliation completely
|
2009-01-03 16:15:38 +01:00
|
|
|
_ -> case E of
|
2003-03-27 21:55:09 +01:00
|
|
|
{JID, role, none, Reason} ->
|
2003-03-23 21:08:44 +01:00
|
|
|
catch send_kickban_presence(
|
2003-03-27 21:55:09 +01:00
|
|
|
JID, Reason, "307", SD),
|
2003-03-23 21:08:44 +01:00
|
|
|
set_role(JID, none, SD);
|
2006-02-06 06:12:54 +01:00
|
|
|
{JID, affiliation, none, Reason} ->
|
|
|
|
case (SD#state.config)#config.members_only of
|
|
|
|
true ->
|
|
|
|
catch send_kickban_presence(
|
|
|
|
JID, Reason, "321", SD),
|
|
|
|
SD1 = set_affiliation(JID, none, SD),
|
|
|
|
set_role(JID, none, SD1);
|
|
|
|
_ ->
|
|
|
|
SD1 = set_affiliation(JID, none, SD),
|
|
|
|
send_update_presence(JID, SD1),
|
|
|
|
SD1
|
|
|
|
end;
|
2003-03-27 21:55:09 +01:00
|
|
|
{JID, affiliation, outcast, Reason} ->
|
2003-03-23 21:08:44 +01:00
|
|
|
catch send_kickban_presence(
|
2003-03-27 21:55:09 +01:00
|
|
|
JID, Reason, "301", SD),
|
2005-05-04 01:07:14 +02:00
|
|
|
set_affiliation_and_reason(
|
|
|
|
JID, outcast, Reason,
|
2003-03-23 21:08:44 +01:00
|
|
|
set_role(JID, none, SD));
|
2008-09-16 16:39:57 +02:00
|
|
|
{JID, affiliation, A, Reason} when
|
2004-12-05 21:54:55 +01:00
|
|
|
(A == admin) or (A == owner) ->
|
2008-09-16 16:39:57 +02:00
|
|
|
SD1 = set_affiliation_and_reason(JID, A, Reason, SD),
|
2003-03-28 21:20:49 +01:00
|
|
|
SD2 = set_role(JID, moderator, SD1),
|
2008-09-16 16:39:57 +02:00
|
|
|
send_update_presence(JID, Reason, SD2),
|
2003-03-28 21:20:49 +01:00
|
|
|
SD2;
|
2008-09-16 16:39:57 +02:00
|
|
|
{JID, affiliation, member, Reason} ->
|
|
|
|
SD1 = set_affiliation_and_reason(
|
|
|
|
JID, member, Reason, SD),
|
2003-03-28 21:20:49 +01:00
|
|
|
SD2 = set_role(JID, participant, SD1),
|
2008-09-16 16:39:57 +02:00
|
|
|
send_update_presence(JID, Reason, SD2),
|
2003-03-28 21:20:49 +01:00
|
|
|
SD2;
|
2008-09-16 16:39:57 +02:00
|
|
|
{JID, role, Role, Reason} ->
|
|
|
|
SD1 = set_role(JID, Role, SD),
|
|
|
|
catch send_new_presence(JID, Reason, SD1),
|
2003-03-23 21:08:44 +01:00
|
|
|
SD1;
|
2006-01-19 03:17:31 +01:00
|
|
|
{JID, affiliation, A, _Reason} ->
|
2003-03-23 21:08:44 +01:00
|
|
|
SD1 = set_affiliation(JID, A, SD),
|
2003-04-13 21:22:46 +02:00
|
|
|
send_update_presence(JID, SD1),
|
2003-03-23 21:08:44 +01:00
|
|
|
SD1
|
|
|
|
end
|
2009-01-03 16:15:38 +01:00
|
|
|
end
|
|
|
|
end
|
2003-03-23 21:08:44 +01:00
|
|
|
) of
|
2003-03-27 21:55:09 +01:00
|
|
|
{'EXIT', ErrReason} ->
|
2005-05-04 01:07:14 +02:00
|
|
|
?ERROR_MSG("MUC ITEMS SET ERR: ~p~n",
|
|
|
|
[ErrReason]),
|
2003-03-23 21:08:44 +01:00
|
|
|
SD;
|
|
|
|
NSD ->
|
|
|
|
NSD
|
|
|
|
end
|
|
|
|
end, StateData, Res),
|
2003-03-27 21:55:09 +01:00
|
|
|
case (NSD#state.config)#config.persistent of
|
|
|
|
true ->
|
2005-04-17 20:08:34 +02:00
|
|
|
mod_muc:store_room(NSD#state.host, NSD#state.room,
|
|
|
|
make_opts(NSD));
|
2003-03-27 21:55:09 +01:00
|
|
|
_ ->
|
|
|
|
ok
|
|
|
|
end,
|
2003-03-23 21:08:44 +01:00
|
|
|
{result, [], NSD};
|
|
|
|
Err ->
|
|
|
|
Err
|
|
|
|
end.
|
|
|
|
|
2007-08-28 16:35:50 +02:00
|
|
|
|
2006-01-19 03:17:31 +01:00
|
|
|
find_changed_items(_UJID, _UAffiliation, _URole, [], _Lang, _StateData, Res) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
{result, Res};
|
2008-12-01 16:01:27 +01:00
|
|
|
find_changed_items(UJID, UAffiliation, URole, [#xmlcdata{} | Items],
|
2004-03-02 22:16:55 +01:00
|
|
|
Lang, StateData, Res) ->
|
|
|
|
find_changed_items(UJID, UAffiliation, URole, Items, Lang, StateData, Res);
|
2003-03-23 21:08:44 +01:00
|
|
|
find_changed_items(UJID, UAffiliation, URole,
|
2008-12-01 16:01:27 +01:00
|
|
|
[#xmlel{name = 'item'} = Item | Items],
|
2004-03-02 22:16:55 +01:00
|
|
|
Lang, StateData, Res) ->
|
2009-01-09 20:18:46 +01:00
|
|
|
TJID = case exmpp_xml:get_attribute_as_binary(Item, 'jid',false) of
|
2008-12-01 16:01:27 +01:00
|
|
|
S when S =/= false ->
|
2009-06-01 18:35:55 +02:00
|
|
|
try exmpp_jid:parse(S) of
|
2008-12-01 16:01:27 +01:00
|
|
|
J ->
|
|
|
|
{value, J}
|
|
|
|
catch
|
|
|
|
_:_ ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ErrText = io_lib:format(
|
|
|
|
translate:translate(
|
|
|
|
Lang,
|
2009-01-19 16:27:07 +01:00
|
|
|
"Jabber ID ~s is invalid"), [S]),
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, ?ERR(Item, 'not-acceptable', Lang, ErrText)}
|
2003-03-23 21:08:44 +01:00
|
|
|
end;
|
|
|
|
_ ->
|
2009-01-21 14:34:26 +01:00
|
|
|
case exmpp_xml:get_attribute_as_list(Item, 'nick', false) of
|
2008-12-01 16:01:27 +01:00
|
|
|
N when N =/= false ->
|
2003-03-23 21:08:44 +01:00
|
|
|
case find_jid_by_nick(N, StateData) of
|
|
|
|
false ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ErrText =
|
|
|
|
io_lib:format(
|
|
|
|
translate:translate(
|
|
|
|
Lang,
|
|
|
|
"Nickname ~s does not exist in the room"),
|
|
|
|
[N]),
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, ?ERR(Item, 'not-acceptable', Lang, ErrText)};
|
2003-03-23 21:08:44 +01:00
|
|
|
J ->
|
|
|
|
{value, J}
|
|
|
|
end;
|
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'bad-request'}
|
2003-03-23 21:08:44 +01:00
|
|
|
end
|
|
|
|
end,
|
|
|
|
case TJID of
|
|
|
|
{value, JID} ->
|
|
|
|
TAffiliation = get_affiliation(JID, StateData),
|
|
|
|
TRole = get_role(JID, StateData),
|
2009-01-09 20:18:46 +01:00
|
|
|
case exmpp_xml:get_attribute_as_binary(Item, 'role',false) of
|
2003-03-23 21:08:44 +01:00
|
|
|
false ->
|
2009-01-09 20:18:46 +01:00
|
|
|
case exmpp_xml:get_attribute_as_binary(Item, 'affiliation', false) of
|
2003-03-23 21:08:44 +01:00
|
|
|
false ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'bad-request'};
|
|
|
|
StrAffiliation ->
|
2009-01-09 20:18:46 +01:00
|
|
|
case catch binary_to_affiliation(StrAffiliation) of
|
2003-03-23 21:08:44 +01:00
|
|
|
{'EXIT', _} ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ErrText1 =
|
|
|
|
io_lib:format(
|
|
|
|
translate:translate(
|
|
|
|
Lang,
|
|
|
|
"Invalid affiliation: ~s"),
|
|
|
|
[StrAffiliation]),
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, ?ERR(Item, 'not-acceptable', Lang, ErrText1)};
|
2003-03-23 21:08:44 +01:00
|
|
|
SAffiliation ->
|
2008-09-16 16:39:57 +02:00
|
|
|
ServiceAf = get_service_affiliation(JID, StateData),
|
2006-03-06 03:30:15 +01:00
|
|
|
CanChangeRA =
|
|
|
|
case can_change_ra(
|
|
|
|
UAffiliation, URole,
|
|
|
|
TAffiliation, TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, SAffiliation,
|
|
|
|
ServiceAf) of
|
2006-03-06 03:30:15 +01:00
|
|
|
nothing ->
|
|
|
|
nothing;
|
|
|
|
true ->
|
|
|
|
true;
|
|
|
|
check_owner ->
|
|
|
|
case search_affiliation(
|
|
|
|
owner, StateData) of
|
|
|
|
[{OJID, _}] ->
|
2008-12-01 16:01:27 +01:00
|
|
|
jlib:short_bare_jid(OJID) /=
|
|
|
|
jlib:short_prepd_bare_jid(UJID);
|
2006-03-06 03:30:15 +01:00
|
|
|
_ ->
|
|
|
|
true
|
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
false
|
|
|
|
end,
|
|
|
|
case CanChangeRA of
|
2003-03-23 21:08:44 +01:00
|
|
|
nothing ->
|
|
|
|
find_changed_items(
|
|
|
|
UJID,
|
|
|
|
UAffiliation, URole,
|
2004-03-02 22:16:55 +01:00
|
|
|
Items, Lang, StateData,
|
2003-03-23 21:08:44 +01:00
|
|
|
Res);
|
|
|
|
true ->
|
|
|
|
find_changed_items(
|
|
|
|
UJID,
|
|
|
|
UAffiliation, URole,
|
2004-03-02 22:16:55 +01:00
|
|
|
Items, Lang, StateData,
|
2009-06-01 18:30:15 +02:00
|
|
|
[{exmpp_jid:bare(JID),
|
2003-03-23 21:08:44 +01:00
|
|
|
affiliation,
|
2003-03-27 21:55:09 +01:00
|
|
|
SAffiliation,
|
2008-12-01 16:01:27 +01:00
|
|
|
exmpp_xml:get_path(
|
|
|
|
Item, [{element, 'reason'},
|
2003-03-27 21:55:09 +01:00
|
|
|
cdata])} | Res]);
|
2006-03-06 03:30:15 +01:00
|
|
|
false ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'not-allowed'}
|
2003-03-23 21:08:44 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end;
|
2008-12-01 16:01:27 +01:00
|
|
|
StrRole ->
|
2009-01-09 20:18:46 +01:00
|
|
|
case catch binary_to_role(StrRole) of
|
2003-03-23 21:08:44 +01:00
|
|
|
{'EXIT', _} ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ErrText1 =
|
|
|
|
io_lib:format(
|
|
|
|
translate:translate(
|
|
|
|
Lang,
|
|
|
|
"Invalid role: ~s"),
|
|
|
|
[StrRole]),
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, ?ERR(Item, 'bad-request', Lang, ErrText1)};
|
2003-03-23 21:08:44 +01:00
|
|
|
SRole ->
|
2008-09-16 16:39:57 +02:00
|
|
|
ServiceAf = get_service_affiliation(JID, StateData),
|
2006-03-06 03:30:15 +01:00
|
|
|
CanChangeRA =
|
|
|
|
case can_change_ra(
|
|
|
|
UAffiliation, URole,
|
|
|
|
TAffiliation, TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, SRole,
|
|
|
|
ServiceAf) of
|
2006-03-06 03:30:15 +01:00
|
|
|
nothing ->
|
|
|
|
nothing;
|
|
|
|
true ->
|
|
|
|
true;
|
|
|
|
check_owner ->
|
|
|
|
case search_affiliation(
|
|
|
|
owner, StateData) of
|
|
|
|
[{OJID, _}] ->
|
2008-12-01 16:01:27 +01:00
|
|
|
jlib:short_bare_jid(OJID) /=
|
|
|
|
jlib:short_prepd_bare_jid(UJID);
|
2006-03-06 03:30:15 +01:00
|
|
|
_ ->
|
|
|
|
true
|
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
false
|
|
|
|
end,
|
|
|
|
case CanChangeRA of
|
2003-03-23 21:08:44 +01:00
|
|
|
nothing ->
|
|
|
|
find_changed_items(
|
|
|
|
UJID,
|
|
|
|
UAffiliation, URole,
|
2004-03-02 22:16:55 +01:00
|
|
|
Items, Lang, StateData,
|
2003-03-23 21:08:44 +01:00
|
|
|
Res);
|
|
|
|
true ->
|
|
|
|
find_changed_items(
|
|
|
|
UJID,
|
|
|
|
UAffiliation, URole,
|
2004-03-02 22:16:55 +01:00
|
|
|
Items, Lang, StateData,
|
2003-03-27 21:55:09 +01:00
|
|
|
[{JID, role, SRole,
|
2008-12-01 16:01:27 +01:00
|
|
|
exmpp_xml:get_path(
|
|
|
|
Item, [{element, 'reason'},
|
2003-03-27 21:55:09 +01:00
|
|
|
cdata])} | Res]);
|
2003-03-23 21:08:44 +01:00
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'not-allowed'}
|
2003-03-23 21:08:44 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end;
|
|
|
|
Err ->
|
|
|
|
Err
|
|
|
|
end;
|
2004-05-17 22:36:41 +02:00
|
|
|
find_changed_items(_UJID, _UAffiliation, _URole, _Items,
|
|
|
|
_Lang, _StateData, _Res) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'bad-request'}.
|
2003-03-23 21:08:44 +01:00
|
|
|
|
|
|
|
|
2008-09-16 16:39:57 +02:00
|
|
|
can_change_ra(_FAffiliation, _FRole,
|
|
|
|
owner, _TRole,
|
|
|
|
affiliation, owner, owner) ->
|
|
|
|
%% A room owner tries to add as persistent owner a
|
|
|
|
%% participant that is already owner because he is MUC admin
|
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(_FAffiliation, _FRole,
|
|
|
|
TAffiliation, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, Value, _ServiceAf)
|
2003-03-23 21:08:44 +01:00
|
|
|
when (TAffiliation == Value) ->
|
|
|
|
nothing;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(_FAffiliation, _FRole,
|
|
|
|
_TAffiliation, TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, Value, _ServiceAf)
|
2003-03-23 21:08:44 +01:00
|
|
|
when (TRole == Value) ->
|
|
|
|
nothing;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(FAffiliation, _FRole,
|
|
|
|
outcast, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, none, _ServiceAf)
|
2003-03-23 21:08:44 +01:00
|
|
|
when (FAffiliation == owner) or (FAffiliation == admin) ->
|
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(FAffiliation, _FRole,
|
|
|
|
outcast, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, member, _ServiceAf)
|
2005-05-02 23:59:39 +02:00
|
|
|
when (FAffiliation == owner) or (FAffiliation == admin) ->
|
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(owner, _FRole,
|
|
|
|
outcast, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, admin, _ServiceAf) ->
|
2005-05-02 23:59:39 +02:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(owner, _FRole,
|
|
|
|
outcast, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, owner, _ServiceAf) ->
|
2005-05-02 23:59:39 +02:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(FAffiliation, _FRole,
|
|
|
|
none, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, outcast, _ServiceAf)
|
2003-03-23 21:08:44 +01:00
|
|
|
when (FAffiliation == owner) or (FAffiliation == admin) ->
|
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(FAffiliation, _FRole,
|
|
|
|
none, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, member, _ServiceAf)
|
2003-03-23 21:08:44 +01:00
|
|
|
when (FAffiliation == owner) or (FAffiliation == admin) ->
|
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(owner, _FRole,
|
|
|
|
none, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, admin, _ServiceAf) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(owner, _FRole,
|
|
|
|
none, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, owner, _ServiceAf) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(FAffiliation, _FRole,
|
|
|
|
member, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, outcast, _ServiceAf)
|
2003-03-23 21:08:44 +01:00
|
|
|
when (FAffiliation == owner) or (FAffiliation == admin) ->
|
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(FAffiliation, _FRole,
|
|
|
|
member, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, none, _ServiceAf)
|
2003-03-23 21:08:44 +01:00
|
|
|
when (FAffiliation == owner) or (FAffiliation == admin) ->
|
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(owner, _FRole,
|
|
|
|
member, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, admin, _ServiceAf) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(owner, _FRole,
|
|
|
|
member, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, owner, _ServiceAf) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(owner, _FRole,
|
|
|
|
admin, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, _Affiliation, _ServiceAf) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(owner, _FRole,
|
|
|
|
owner, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, _Affiliation, _ServiceAf) ->
|
2006-03-06 03:30:15 +01:00
|
|
|
check_owner;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(_FAffiliation, _FRole,
|
|
|
|
_TAffiliation, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
affiliation, _Value, _ServiceAf) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
false;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(_FAffiliation, moderator,
|
|
|
|
_TAffiliation, visitor,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, none, _ServiceAf) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(_FAffiliation, moderator,
|
|
|
|
_TAffiliation, visitor,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, participant, _ServiceAf) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(FAffiliation, _FRole,
|
|
|
|
_TAffiliation, visitor,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, moderator, _ServiceAf)
|
2003-03-23 21:08:44 +01:00
|
|
|
when (FAffiliation == owner) or (FAffiliation == admin) ->
|
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(_FAffiliation, moderator,
|
|
|
|
_TAffiliation, participant,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, none, _ServiceAf) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(_FAffiliation, moderator,
|
|
|
|
_TAffiliation, participant,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, visitor, _ServiceAf) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(FAffiliation, _FRole,
|
|
|
|
_TAffiliation, participant,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, moderator, _ServiceAf)
|
2003-03-23 21:08:44 +01:00
|
|
|
when (FAffiliation == owner) or (FAffiliation == admin) ->
|
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(_FAffiliation, _FRole,
|
2005-05-02 23:59:39 +02:00
|
|
|
owner, moderator,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, visitor, _ServiceAf) ->
|
2005-05-02 23:59:39 +02:00
|
|
|
false;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(owner, _FRole,
|
|
|
|
_TAffiliation, moderator,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, visitor, _ServiceAf) ->
|
2005-05-02 23:59:39 +02:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(_FAffiliation, _FRole,
|
2005-05-02 23:59:39 +02:00
|
|
|
admin, moderator,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, visitor, _ServiceAf) ->
|
2005-05-02 23:59:39 +02:00
|
|
|
false;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(admin, _FRole,
|
|
|
|
_TAffiliation, moderator,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, visitor, _ServiceAf) ->
|
2005-05-02 23:59:39 +02:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(_FAffiliation, _FRole,
|
2004-01-17 21:26:57 +01:00
|
|
|
owner, moderator,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, participant, _ServiceAf) ->
|
2004-01-17 21:26:57 +01:00
|
|
|
false;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(owner, _FRole,
|
|
|
|
_TAffiliation, moderator,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, participant, _ServiceAf) ->
|
2004-01-17 21:26:57 +01:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(_FAffiliation, _FRole,
|
2004-01-17 21:26:57 +01:00
|
|
|
admin, moderator,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, participant, _ServiceAf) ->
|
2004-01-17 21:26:57 +01:00
|
|
|
false;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(admin, _FRole,
|
|
|
|
_TAffiliation, moderator,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, participant, _ServiceAf) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
true;
|
2006-01-19 03:17:31 +01:00
|
|
|
can_change_ra(_FAffiliation, _FRole,
|
|
|
|
_TAffiliation, _TRole,
|
2008-09-16 16:39:57 +02:00
|
|
|
role, _Value, _ServiceAf) ->
|
2003-03-23 21:08:44 +01:00
|
|
|
false.
|
|
|
|
|
|
|
|
|
2003-04-13 21:22:46 +02:00
|
|
|
send_kickban_presence(JID, Reason, Code, StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
LJID = jlib:short_prepd_jid(JID),
|
2003-04-13 21:22:46 +02:00
|
|
|
LJIDs = case LJID of
|
2008-12-01 16:01:27 +01:00
|
|
|
{U, S, undefined} ->
|
2003-04-13 21:22:46 +02:00
|
|
|
?DICT:fold(
|
|
|
|
fun(J, _, Js) ->
|
|
|
|
case J of
|
|
|
|
{U, S, _} ->
|
|
|
|
[J | Js];
|
|
|
|
_ ->
|
|
|
|
Js
|
|
|
|
end
|
|
|
|
end, [], StateData#state.users);
|
|
|
|
_ ->
|
|
|
|
case ?DICT:is_key(LJID, StateData#state.users) of
|
|
|
|
true ->
|
|
|
|
[LJID];
|
|
|
|
_ ->
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
lists:foreach(fun(J) ->
|
2006-03-14 05:26:15 +01:00
|
|
|
{ok, #user{nick = Nick}} =
|
|
|
|
?DICT:find(J, StateData#state.users),
|
|
|
|
add_to_log(kickban, {Nick, Reason, Code}, StateData),
|
2007-12-03 11:47:42 +01:00
|
|
|
tab_remove_online_user(J, StateData),
|
2003-04-13 21:22:46 +02:00
|
|
|
send_kickban_presence1(J, Reason, Code, StateData)
|
|
|
|
end, LJIDs).
|
|
|
|
|
|
|
|
send_kickban_presence1(UJID, Reason, Code, StateData) ->
|
2006-01-19 03:17:31 +01:00
|
|
|
{ok, #user{jid = _RealJID,
|
2008-12-01 16:01:27 +01:00
|
|
|
nick = Nick}} = ?DICT:find(UJID, StateData#state.users),
|
|
|
|
{N,D,R} = UJID,
|
2009-06-01 18:26:00 +02:00
|
|
|
Affiliation = get_affiliation(exmpp_jid:make(N,D,R), StateData),
|
2009-01-09 20:18:46 +01:00
|
|
|
SAffiliation = affiliation_to_binary(Affiliation),
|
2003-03-23 21:08:44 +01:00
|
|
|
lists:foreach(
|
2006-01-19 03:17:31 +01:00
|
|
|
fun({_LJID, Info}) ->
|
2009-01-21 14:34:26 +01:00
|
|
|
ItemAttrs = [?XMLATTR('affiliation', SAffiliation),
|
|
|
|
?XMLATTR('role', <<"none">>)],
|
2003-03-27 21:55:09 +01:00
|
|
|
ItemEls = case Reason of
|
2008-12-01 16:01:27 +01:00
|
|
|
<<>> ->
|
2003-03-27 21:55:09 +01:00
|
|
|
[];
|
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
[#xmlel{name = 'reason',
|
|
|
|
children = [#xmlcdata{cdata = Reason}]}]
|
|
|
|
end,
|
|
|
|
Packet =
|
|
|
|
#xmlel{ns = ?NS_JABBER_CLIENT,
|
|
|
|
name = 'presence',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('type', <<"unavailable">>)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{ns = ?NS_MUC_USER, name = 'x',
|
|
|
|
children = [
|
|
|
|
#xmlel{ns = ?NS_MUC_USER, name = 'item',
|
|
|
|
attrs = ItemAttrs,
|
|
|
|
children = ItemEls},
|
|
|
|
#xmlel{ns = ?NS_MUC_USER, name = 'status',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('code',
|
|
|
|
Code)]}]}]},
|
2003-03-23 21:08:44 +01:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(StateData#state.jid, Nick),
|
2003-03-23 21:08:44 +01:00
|
|
|
Info#user.jid,
|
|
|
|
Packet)
|
|
|
|
end, ?DICT:to_list(StateData#state.users)).
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-03-25 22:03:35 +01:00
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
% Owner stuff
|
|
|
|
|
2004-02-26 23:00:04 +01:00
|
|
|
process_iq_owner(From, set, Lang, SubEl, StateData) ->
|
2003-03-25 22:03:35 +01:00
|
|
|
FAffiliation = get_affiliation(From, StateData),
|
|
|
|
case FAffiliation of
|
|
|
|
owner ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case exmpp_xml:get_child_elements(SubEl) of
|
|
|
|
[#xmlel{ns = XMLNS, name = 'x'} = XEl] ->
|
2009-01-09 20:18:46 +01:00
|
|
|
case {XMLNS, exmpp_xml:get_attribute_as_binary(XEl, 'type',false)} of
|
|
|
|
{?NS_DATA_FORMS, <<"cancel">>} ->
|
2003-03-27 21:55:09 +01:00
|
|
|
{result, [], StateData};
|
2009-01-09 20:18:46 +01:00
|
|
|
{?NS_DATA_FORMS, <<"submit">>} ->
|
2009-04-22 14:05:10 +02:00
|
|
|
case is_allowed_log_change(XEl, StateData, From)
|
|
|
|
andalso
|
|
|
|
is_allowed_persistent_change(XEl, StateData,
|
|
|
|
From)
|
|
|
|
andalso
|
|
|
|
is_allowed_room_name_desc_limits(XEl,
|
2009-08-15 22:11:24 +02:00
|
|
|
StateData)
|
|
|
|
andalso
|
|
|
|
is_password_settings_correct(XEl, StateData) of
|
2009-04-22 14:05:10 +02:00
|
|
|
true -> set_config(XEl, StateData);
|
2009-08-15 22:11:10 +02:00
|
|
|
false -> {error, 'not-acceptable'}
|
2009-04-22 14:05:10 +02:00
|
|
|
end;
|
2003-03-25 22:03:35 +01:00
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'bad-request'}
|
2003-03-25 22:03:35 +01:00
|
|
|
end;
|
2008-12-01 16:01:27 +01:00
|
|
|
[#xmlel{name = 'destroy'} = SubEl1] ->
|
2008-01-16 11:08:17 +01:00
|
|
|
?INFO_MSG("Destroyed MUC room ~s by the owner ~s",
|
2009-06-01 18:59:08 +02:00
|
|
|
[exmpp_jid:to_binary(StateData#state.jid), exmpp_jid:to_binary(From)]),
|
2009-12-29 15:44:12 +01:00
|
|
|
add_to_log(room_existence, destroyed, StateData),
|
2006-01-19 03:17:31 +01:00
|
|
|
destroy_room(SubEl1, StateData);
|
2003-03-28 21:20:49 +01:00
|
|
|
Items ->
|
2004-03-02 22:16:55 +01:00
|
|
|
process_admin_items_set(From, Items, Lang, StateData)
|
2003-03-25 22:03:35 +01:00
|
|
|
end;
|
|
|
|
_ ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ErrText = "Owner privileges required",
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, ?ERR(SubEl, 'forbidden', Lang, ErrText)}
|
2003-03-25 22:03:35 +01:00
|
|
|
end;
|
|
|
|
|
2004-02-26 23:00:04 +01:00
|
|
|
process_iq_owner(From, get, Lang, SubEl, StateData) ->
|
2003-03-25 22:03:35 +01:00
|
|
|
FAffiliation = get_affiliation(From, StateData),
|
|
|
|
case FAffiliation of
|
|
|
|
owner ->
|
2008-12-01 16:01:27 +01:00
|
|
|
case exmpp_xml:get_child_elements(SubEl) of
|
2003-03-25 22:03:35 +01:00
|
|
|
[] ->
|
2006-03-14 05:26:15 +01:00
|
|
|
get_config(Lang, StateData, From);
|
2003-03-28 21:20:49 +01:00
|
|
|
[Item] ->
|
2009-01-09 20:18:46 +01:00
|
|
|
case exmpp_xml:get_attribute_as_binary(Item, 'affiliation',false) of
|
2003-03-28 21:20:49 +01:00
|
|
|
false ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'bad-request'};
|
|
|
|
StrAffiliation ->
|
2009-01-09 20:18:46 +01:00
|
|
|
case catch binary_to_affiliation(StrAffiliation) of
|
2003-03-28 21:20:49 +01:00
|
|
|
{'EXIT', _} ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ErrText =
|
|
|
|
io_lib:format(
|
|
|
|
translate:translate(
|
|
|
|
Lang,
|
|
|
|
"Invalid affiliation: ~s"),
|
|
|
|
[StrAffiliation]),
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, ?ERR(SubEl, 'not-acceptable', Lang, ErrText)};
|
2003-03-28 21:20:49 +01:00
|
|
|
SAffiliation ->
|
|
|
|
Items = items_with_affiliation(
|
|
|
|
SAffiliation, StateData),
|
|
|
|
{result, Items, StateData}
|
|
|
|
end
|
|
|
|
end;
|
2003-03-25 22:03:35 +01:00
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'feature-not-implemented'}
|
2003-03-25 22:03:35 +01:00
|
|
|
end;
|
|
|
|
_ ->
|
2004-03-02 22:16:55 +01:00
|
|
|
ErrText = "Owner privileges required",
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, ?ERR(SubEl, 'forbidden', Lang, ErrText)}
|
2003-03-25 22:03:35 +01:00
|
|
|
end.
|
|
|
|
|
2009-04-22 14:05:10 +02:00
|
|
|
is_allowed_log_change(XEl, StateData, From) ->
|
2006-08-27 19:46:27 +02:00
|
|
|
case lists:keymember("muc#roomconfig_enablelogging", 1,
|
|
|
|
jlib:parse_xdata_submit(XEl)) of
|
2006-03-14 05:26:15 +01:00
|
|
|
false ->
|
2009-04-22 14:05:10 +02:00
|
|
|
true;
|
2006-03-14 05:26:15 +01:00
|
|
|
true ->
|
2009-04-22 14:05:10 +02:00
|
|
|
(allow == mod_muc_log:check_access_log(
|
|
|
|
StateData#state.server_host, From))
|
2006-03-14 05:26:15 +01:00
|
|
|
end.
|
2003-03-25 22:03:35 +01:00
|
|
|
|
2009-04-22 14:05:10 +02:00
|
|
|
is_allowed_persistent_change(XEl, StateData, From) ->
|
2007-06-20 13:25:19 +02:00
|
|
|
case lists:keymember("muc#roomconfig_persistentroom", 1,
|
|
|
|
jlib:parse_xdata_submit(XEl)) of
|
|
|
|
false ->
|
2009-04-22 14:05:10 +02:00
|
|
|
true;
|
2007-06-20 13:25:19 +02:00
|
|
|
true ->
|
|
|
|
{_AccessRoute, _AccessCreate, _AccessAdmin, AccessPersistent} = StateData#state.access,
|
2009-05-03 13:26:18 +02:00
|
|
|
(allow == acl:match_rule(StateData#state.server_host, AccessPersistent, From))
|
2007-06-20 13:25:19 +02:00
|
|
|
end.
|
2003-03-25 22:03:35 +01:00
|
|
|
|
2009-04-22 14:05:10 +02:00
|
|
|
%% Check if the Room Name and Room Description defined in the Data Form
|
|
|
|
%% are conformant to the configured limits
|
|
|
|
is_allowed_room_name_desc_limits(XEl, StateData) ->
|
|
|
|
IsNameAccepted =
|
|
|
|
case lists:keysearch("muc#roomconfig_roomname", 1,
|
|
|
|
jlib:parse_xdata_submit(XEl)) of
|
|
|
|
{value, {_, [N]}} ->
|
|
|
|
length(N) =< gen_mod:get_module_opt(StateData#state.server_host,
|
|
|
|
mod_muc, max_room_name,
|
|
|
|
infinite);
|
|
|
|
_ ->
|
|
|
|
true
|
|
|
|
end,
|
|
|
|
IsDescAccepted =
|
|
|
|
case lists:keysearch("muc#roomconfig_roomdesc", 1,
|
|
|
|
jlib:parse_xdata_submit(XEl)) of
|
|
|
|
{value, {_, [D]}} ->
|
|
|
|
length(D) =< gen_mod:get_module_opt(StateData#state.server_host,
|
|
|
|
mod_muc, max_room_desc,
|
|
|
|
infinite);
|
|
|
|
_ ->
|
|
|
|
true
|
|
|
|
end,
|
|
|
|
IsNameAccepted and IsDescAccepted.
|
|
|
|
|
2009-08-15 22:11:24 +02:00
|
|
|
%% Return false if:
|
|
|
|
%% "the password for a password-protected room is blank"
|
|
|
|
is_password_settings_correct(XEl, StateData) ->
|
|
|
|
Config = StateData#state.config,
|
|
|
|
OldProtected = Config#config.password_protected,
|
|
|
|
OldPassword = Config#config.password,
|
|
|
|
NewProtected =
|
|
|
|
case lists:keysearch("muc#roomconfig_passwordprotectedroom", 1,
|
|
|
|
jlib:parse_xdata_submit(XEl)) of
|
|
|
|
{value, {_, ["1"]}} ->
|
|
|
|
true;
|
|
|
|
{value, {_, ["0"]}} ->
|
|
|
|
false;
|
|
|
|
_ ->
|
|
|
|
undefined
|
|
|
|
end,
|
|
|
|
NewPassword =
|
|
|
|
case lists:keysearch("muc#roomconfig_roomsecret", 1,
|
|
|
|
jlib:parse_xdata_submit(XEl)) of
|
|
|
|
{value, {_, [P]}} ->
|
|
|
|
P;
|
|
|
|
_ ->
|
|
|
|
undefined
|
|
|
|
end,
|
|
|
|
case {OldProtected, NewProtected, OldPassword, NewPassword} of
|
|
|
|
{true, undefined, "", undefined} ->
|
|
|
|
false;
|
|
|
|
{true, undefined, _, ""} ->
|
|
|
|
false;
|
|
|
|
{_, true , "", undefined} ->
|
|
|
|
false;
|
|
|
|
{_, true, _, ""} ->
|
|
|
|
false;
|
|
|
|
_ ->
|
|
|
|
true
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
2003-03-25 22:03:35 +01:00
|
|
|
-define(XFIELD(Type, Label, Var, Val),
|
2008-12-01 16:01:27 +01:00
|
|
|
#xmlel{name = 'field',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('type', Type),
|
|
|
|
?XMLATTR('label', translate:translate(Lang, Label)),
|
|
|
|
?XMLATTR('var', Var)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{name = 'value',
|
|
|
|
children = [#xmlcdata{cdata = Val} ]}]}).
|
|
|
|
|
2003-03-25 22:03:35 +01:00
|
|
|
|
|
|
|
-define(BOOLXFIELD(Label, Var, Val),
|
|
|
|
?XFIELD("boolean", Label, Var,
|
|
|
|
case Val of
|
|
|
|
true -> "1";
|
|
|
|
_ -> "0"
|
|
|
|
end)).
|
|
|
|
|
2003-03-27 21:55:09 +01:00
|
|
|
-define(STRINGXFIELD(Label, Var, Val),
|
|
|
|
?XFIELD("text-single", Label, Var, Val)).
|
|
|
|
|
2003-08-15 21:17:12 +02:00
|
|
|
-define(PRIVATEXFIELD(Label, Var, Val),
|
|
|
|
?XFIELD("text-private", Label, Var, Val)).
|
|
|
|
|
2008-12-01 16:53:30 +01:00
|
|
|
|
|
|
|
get_default_room_maxusers(RoomState) ->
|
2008-12-08 13:02:27 +01:00
|
|
|
DefRoomOpts = gen_mod:get_module_opt(RoomState#state.server_host, mod_muc, default_room_options, []),
|
2008-12-01 16:53:30 +01:00
|
|
|
RoomState2 = set_opts(DefRoomOpts, RoomState),
|
|
|
|
(RoomState2#state.config)#config.max_users.
|
|
|
|
|
2006-03-14 05:26:15 +01:00
|
|
|
get_config(Lang, StateData, From) ->
|
2007-06-20 13:25:19 +02:00
|
|
|
{_AccessRoute, _AccessCreate, _AccessAdmin, AccessPersistent} = StateData#state.access,
|
2007-08-29 19:54:45 +02:00
|
|
|
ServiceMaxUsers = get_service_max_users(StateData),
|
2008-12-01 16:53:30 +01:00
|
|
|
DefaultRoomMaxUsers = get_default_room_maxusers(StateData),
|
2003-03-25 22:03:35 +01:00
|
|
|
Config = StateData#state.config,
|
2008-12-01 16:53:30 +01:00
|
|
|
{MaxUsersRoomInteger, MaxUsersRoomString} =
|
|
|
|
case get_max_users(StateData) of
|
|
|
|
N when is_integer(N) ->
|
|
|
|
{N, erlang:integer_to_list(N)};
|
|
|
|
_ -> {0, "none"}
|
|
|
|
end,
|
2003-03-25 22:03:35 +01:00
|
|
|
Res =
|
2008-12-01 16:01:27 +01:00
|
|
|
[#xmlel{name = 'title', children = [ #xmlcdata{cdata =
|
2009-05-26 19:20:09 +02:00
|
|
|
io_lib:format(translate:translate(Lang, "Configuration of room ~s"),
|
2009-06-01 18:52:14 +02:00
|
|
|
[exmpp_jid:to_list(StateData#state.jid)])
|
2009-05-26 19:20:09 +02:00
|
|
|
}]},
|
2009-01-21 14:34:26 +01:00
|
|
|
#xmlel{name = 'field', attrs = [?XMLATTR('type', <<"hidden">>),
|
|
|
|
?XMLATTR('var', <<"FORM_TYPE">>)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{name = 'value', children = [#xmlcdata{cdata =
|
|
|
|
<<"http://jabber.org/protocol/muc#roomconfig">>
|
|
|
|
}]}]},
|
2004-02-26 23:00:04 +01:00
|
|
|
?STRINGXFIELD("Room title",
|
2006-08-04 03:57:51 +02:00
|
|
|
"muc#roomconfig_roomname",
|
2008-07-11 14:48:27 +02:00
|
|
|
Config#config.title),
|
|
|
|
?STRINGXFIELD("Room description",
|
|
|
|
"muc#roomconfig_roomdesc",
|
|
|
|
Config#config.description)
|
2007-06-20 13:25:19 +02:00
|
|
|
] ++
|
|
|
|
case acl:match_rule(StateData#state.server_host, AccessPersistent, From) of
|
|
|
|
allow ->
|
|
|
|
[?BOOLXFIELD(
|
|
|
|
"Make room persistent",
|
|
|
|
"muc#roomconfig_persistentroom",
|
|
|
|
Config#config.persistent)];
|
|
|
|
_ -> []
|
|
|
|
end ++ [
|
2006-01-19 03:17:31 +01:00
|
|
|
?BOOLXFIELD("Make room public searchable",
|
2006-08-04 03:57:51 +02:00
|
|
|
"muc#roomconfig_publicroom",
|
2003-03-25 22:03:35 +01:00
|
|
|
Config#config.public),
|
2006-01-19 03:17:31 +01:00
|
|
|
?BOOLXFIELD("Make participants list public",
|
2003-05-15 20:16:13 +02:00
|
|
|
"public_list",
|
|
|
|
Config#config.public_list),
|
2006-01-19 03:17:31 +01:00
|
|
|
?BOOLXFIELD("Make room password protected",
|
2006-08-04 03:57:51 +02:00
|
|
|
"muc#roomconfig_passwordprotectedroom",
|
2003-03-25 22:03:35 +01:00
|
|
|
Config#config.password_protected),
|
2003-08-15 21:17:12 +02:00
|
|
|
?PRIVATEXFIELD("Password",
|
2006-08-04 03:57:51 +02:00
|
|
|
"muc#roomconfig_roomsecret",
|
2003-08-15 21:17:12 +02:00
|
|
|
case Config#config.password_protected of
|
|
|
|
true -> Config#config.password;
|
|
|
|
false -> ""
|
|
|
|
end),
|
2008-12-01 16:01:27 +01:00
|
|
|
#xmlel{name = 'field', attrs = [
|
2009-01-21 14:34:26 +01:00
|
|
|
?XMLATTR('type', <<"list-single">>),
|
|
|
|
?XMLATTR('label', translate:translate(Lang,
|
|
|
|
"Maximum Number of Occupants")),
|
|
|
|
?XMLATTR('var', <<"muc#roomconfig_maxusers">>)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{name = 'value',
|
|
|
|
children = [#xmlcdata{cdata =
|
2008-12-01 16:53:30 +01:00
|
|
|
MaxUsersRoomString}]}] ++
|
2007-08-29 19:54:45 +02:00
|
|
|
if
|
|
|
|
is_integer(ServiceMaxUsers) -> [];
|
|
|
|
true ->
|
2009-01-21 14:34:26 +01:00
|
|
|
[#xmlel{name = 'option', attrs = [?XMLATTR('label',
|
|
|
|
translate:translate(Lang, "No limit"))],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{name = 'value', children = [#xmlcdata{
|
|
|
|
cdata = <<"none">>}]}]}]
|
2007-08-29 19:54:45 +02:00
|
|
|
end ++
|
2009-01-21 14:34:26 +01:00
|
|
|
[#xmlel{name = 'option', attrs = [?XMLATTR('label', N)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{name = 'value', children = [
|
|
|
|
#xmlcdata{cdata = erlang:integer_to_list(N)}]}]} ||
|
2008-12-01 16:53:30 +01:00
|
|
|
N <- lists:usort([ServiceMaxUsers, DefaultRoomMaxUsers, MaxUsersRoomInteger |
|
|
|
|
?MAX_USERS_DEFAULT_LIST]), N =< ServiceMaxUsers]},
|
2008-12-01 16:01:27 +01:00
|
|
|
#xmlel{name = 'field', attrs = [
|
2009-01-21 14:34:26 +01:00
|
|
|
?XMLATTR('type', <<"list-single">>),
|
|
|
|
?XMLATTR('label',
|
|
|
|
translate:translate(Lang, "Present real Jabber IDs to")),
|
|
|
|
?XMLATTR('var', <<"muc#roomconfig_whois">>)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{name = 'value',
|
|
|
|
children = [#xmlcdata{cdata =
|
|
|
|
if Config#config.anonymous -> <<"moderators">>;
|
|
|
|
true -> <<"anyone">>
|
|
|
|
end}]},
|
|
|
|
#xmlel{name = 'option', attrs = [
|
2009-01-21 14:34:26 +01:00
|
|
|
?XMLATTR('label',
|
|
|
|
translate:translate(Lang, "moderators only"))],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{name = 'value',
|
|
|
|
children = [#xmlcdata{cdata =
|
|
|
|
<<"moderators">>}]}]},
|
|
|
|
#xmlel{name = 'option', attrs = [
|
2009-01-21 14:34:26 +01:00
|
|
|
?XMLATTR('label',
|
|
|
|
translate:translate(Lang, "anyone"))],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{name = 'value',
|
|
|
|
children = [#xmlcdata{cdata =
|
|
|
|
<<"anyone">>}]}]}]},
|
|
|
|
?BOOLXFIELD("Make room members-only",
|
|
|
|
"muc#roomconfig_membersonly",
|
|
|
|
Config#config.members_only),
|
|
|
|
?BOOLXFIELD("Make room moderated",
|
|
|
|
"muc#roomconfig_moderatedroom",
|
|
|
|
Config#config.moderated),
|
|
|
|
?BOOLXFIELD("Default users as participants",
|
|
|
|
"members_by_default",
|
|
|
|
Config#config.members_by_default),
|
2009-04-08 21:04:13 +02:00
|
|
|
?BOOLXFIELD("Allow users to change the subject",
|
2008-12-01 16:01:27 +01:00
|
|
|
"muc#roomconfig_changesubject",
|
|
|
|
Config#config.allow_change_subj),
|
|
|
|
?BOOLXFIELD("Allow users to send private messages",
|
|
|
|
"allow_private_messages",
|
|
|
|
Config#config.allow_private_messages),
|
|
|
|
?BOOLXFIELD("Allow users to query other users",
|
|
|
|
"allow_query_users",
|
|
|
|
Config#config.allow_query_users),
|
|
|
|
?BOOLXFIELD("Allow users to send invites",
|
|
|
|
"muc#roomconfig_allowinvites",
|
|
|
|
Config#config.allow_user_invites),
|
|
|
|
?BOOLXFIELD("Allow visitors to send status text in presence updates",
|
|
|
|
"muc#roomconfig_allowvisitorstatus",
|
|
|
|
Config#config.allow_visitor_status),
|
|
|
|
?BOOLXFIELD("Allow visitors to change nickname",
|
|
|
|
"muc#roomconfig_allowvisitornickchange",
|
|
|
|
Config#config.allow_visitor_nickchange)
|
2009-05-26 19:20:09 +02:00
|
|
|
] ++
|
|
|
|
case ejabberd_captcha:is_feature_available() of
|
|
|
|
true ->
|
|
|
|
[?BOOLXFIELD("Make room captcha protected",
|
|
|
|
"captcha_protected",
|
|
|
|
Config#config.captcha_protected)];
|
|
|
|
false -> []
|
|
|
|
end ++
|
2006-03-14 05:26:15 +01:00
|
|
|
case mod_muc_log:check_access_log(
|
2008-12-01 16:01:27 +01:00
|
|
|
StateData#state.server_host, From) of
|
2006-03-14 05:26:15 +01:00
|
|
|
allow ->
|
|
|
|
[?BOOLXFIELD(
|
|
|
|
"Enable logging",
|
2006-08-04 03:57:51 +02:00
|
|
|
"muc#roomconfig_enablelogging",
|
2006-03-14 05:26:15 +01:00
|
|
|
Config#config.logging)];
|
|
|
|
_ -> []
|
|
|
|
end,
|
2008-12-01 16:01:27 +01:00
|
|
|
{result , [#xmlel{name = 'instructions', children = [
|
|
|
|
#xmlcdata{cdata = translate:translate(Lang,
|
|
|
|
"You need an x:data capable client to configure room")}]},
|
|
|
|
#xmlel{ns = ?NS_DATA_FORMS, name = 'x',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('type', <<"form">>)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = Res}],
|
|
|
|
StateData}.
|
2003-03-25 22:03:35 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
set_config(XEl, StateData) ->
|
|
|
|
XData = jlib:parse_xdata_submit(XEl),
|
|
|
|
case XData of
|
|
|
|
invalid ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'bad-request'};
|
2003-03-25 22:03:35 +01:00
|
|
|
_ ->
|
|
|
|
case set_xoption(XData, StateData#state.config) of
|
|
|
|
#config{} = Config ->
|
2006-03-14 05:26:15 +01:00
|
|
|
Res = change_config(Config, StateData),
|
|
|
|
{result, _, NSD} = Res,
|
2009-07-21 20:35:20 +02:00
|
|
|
Type = case {(StateData#state.config)#config.logging,
|
|
|
|
Config#config.logging} of
|
|
|
|
{true, false} ->
|
|
|
|
roomconfig_change_disabledlogging;
|
|
|
|
{false, true} ->
|
|
|
|
roomconfig_change_enabledlogging;
|
|
|
|
{_, _} ->
|
|
|
|
roomconfig_change
|
|
|
|
end,
|
|
|
|
Users = [{U#user.jid, U#user.nick, U#user.role} ||
|
|
|
|
{_, U} <- ?DICT:to_list(StateData#state.users)],
|
|
|
|
add_to_log(Type, Users, NSD),
|
2006-03-14 05:26:15 +01:00
|
|
|
Res;
|
2003-03-25 22:03:35 +01:00
|
|
|
Err ->
|
|
|
|
Err
|
|
|
|
end
|
|
|
|
end.
|
|
|
|
|
|
|
|
-define(SET_BOOL_XOPT(Opt, Val),
|
|
|
|
case Val of
|
|
|
|
"0" -> set_xoption(Opts, Config#config{Opt = false});
|
2006-02-06 06:12:54 +01:00
|
|
|
"false" -> set_xoption(Opts, Config#config{Opt = false});
|
2003-03-25 22:03:35 +01:00
|
|
|
"1" -> set_xoption(Opts, Config#config{Opt = true});
|
2006-02-06 06:12:54 +01:00
|
|
|
"true" -> set_xoption(Opts, Config#config{Opt = true});
|
2008-12-01 16:01:27 +01:00
|
|
|
_ -> {error, 'bad-request'}
|
2003-03-25 22:03:35 +01:00
|
|
|
end).
|
|
|
|
|
2007-08-29 19:54:45 +02:00
|
|
|
-define(SET_NAT_XOPT(Opt, Val),
|
|
|
|
case catch list_to_integer(Val) of
|
|
|
|
I when is_integer(I),
|
|
|
|
I > 0 ->
|
|
|
|
set_xoption(Opts, Config#config{Opt = I});
|
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'bad-request'}
|
2007-08-29 19:54:45 +02:00
|
|
|
end).
|
|
|
|
|
2003-03-27 21:55:09 +01:00
|
|
|
-define(SET_STRING_XOPT(Opt, Val),
|
|
|
|
set_xoption(Opts, Config#config{Opt = Val})).
|
|
|
|
|
2003-03-25 22:03:35 +01:00
|
|
|
|
|
|
|
set_xoption([], Config) ->
|
|
|
|
Config;
|
2006-08-04 03:57:51 +02:00
|
|
|
set_xoption([{"muc#roomconfig_roomname", [Val]} | Opts], Config) ->
|
2003-03-27 21:55:09 +01:00
|
|
|
?SET_STRING_XOPT(title, Val);
|
2008-07-11 14:48:27 +02:00
|
|
|
set_xoption([{"muc#roomconfig_roomdesc", [Val]} | Opts], Config) ->
|
|
|
|
?SET_STRING_XOPT(description, Val);
|
2006-08-04 03:57:51 +02:00
|
|
|
set_xoption([{"muc#roomconfig_changesubject", [Val]} | Opts], Config) ->
|
2003-03-25 22:03:35 +01:00
|
|
|
?SET_BOOL_XOPT(allow_change_subj, Val);
|
|
|
|
set_xoption([{"allow_query_users", [Val]} | Opts], Config) ->
|
|
|
|
?SET_BOOL_XOPT(allow_query_users, Val);
|
|
|
|
set_xoption([{"allow_private_messages", [Val]} | Opts], Config) ->
|
|
|
|
?SET_BOOL_XOPT(allow_private_messages, Val);
|
2008-09-16 16:39:57 +02:00
|
|
|
set_xoption([{"muc#roomconfig_allowvisitorstatus", [Val]} | Opts], Config) ->
|
|
|
|
?SET_BOOL_XOPT(allow_visitor_status, Val);
|
|
|
|
set_xoption([{"muc#roomconfig_allowvisitornickchange", [Val]} | Opts], Config) ->
|
|
|
|
?SET_BOOL_XOPT(allow_visitor_nickchange, Val);
|
2006-08-04 03:57:51 +02:00
|
|
|
set_xoption([{"muc#roomconfig_publicroom", [Val]} | Opts], Config) ->
|
2003-03-25 22:03:35 +01:00
|
|
|
?SET_BOOL_XOPT(public, Val);
|
2003-05-15 20:16:13 +02:00
|
|
|
set_xoption([{"public_list", [Val]} | Opts], Config) ->
|
|
|
|
?SET_BOOL_XOPT(public_list, Val);
|
2006-08-04 03:57:51 +02:00
|
|
|
set_xoption([{"muc#roomconfig_persistentroom", [Val]} | Opts], Config) ->
|
2003-03-25 22:03:35 +01:00
|
|
|
?SET_BOOL_XOPT(persistent, Val);
|
2006-08-04 03:57:51 +02:00
|
|
|
set_xoption([{"muc#roomconfig_moderatedroom", [Val]} | Opts], Config) ->
|
2003-03-25 22:03:35 +01:00
|
|
|
?SET_BOOL_XOPT(moderated, Val);
|
|
|
|
set_xoption([{"members_by_default", [Val]} | Opts], Config) ->
|
|
|
|
?SET_BOOL_XOPT(members_by_default, Val);
|
2006-08-04 03:57:51 +02:00
|
|
|
set_xoption([{"muc#roomconfig_membersonly", [Val]} | Opts], Config) ->
|
2003-03-25 22:03:35 +01:00
|
|
|
?SET_BOOL_XOPT(members_only, Val);
|
2009-05-26 19:20:09 +02:00
|
|
|
set_xoption([{"captcha_protected", [Val]} | Opts], Config) ->
|
|
|
|
?SET_BOOL_XOPT(captcha_protected, Val);
|
2006-08-04 03:57:51 +02:00
|
|
|
set_xoption([{"muc#roomconfig_allowinvites", [Val]} | Opts], Config) ->
|
2003-03-25 22:03:35 +01:00
|
|
|
?SET_BOOL_XOPT(allow_user_invites, Val);
|
2006-08-04 03:57:51 +02:00
|
|
|
set_xoption([{"muc#roomconfig_passwordprotectedroom", [Val]} | Opts], Config) ->
|
2003-03-25 22:03:35 +01:00
|
|
|
?SET_BOOL_XOPT(password_protected, Val);
|
2006-08-04 03:57:51 +02:00
|
|
|
set_xoption([{"muc#roomconfig_roomsecret", [Val]} | Opts], Config) ->
|
2003-08-15 21:17:12 +02:00
|
|
|
?SET_STRING_XOPT(password, Val);
|
2003-03-25 22:03:35 +01:00
|
|
|
set_xoption([{"anonymous", [Val]} | Opts], Config) ->
|
|
|
|
?SET_BOOL_XOPT(anonymous, Val);
|
2006-08-04 03:57:51 +02:00
|
|
|
set_xoption([{"muc#roomconfig_whois", [Val]} | Opts], Config) ->
|
|
|
|
case Val of
|
|
|
|
"moderators" ->
|
|
|
|
?SET_BOOL_XOPT(anonymous, "1");
|
|
|
|
"anyone" ->
|
|
|
|
?SET_BOOL_XOPT(anonymous, "0");
|
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'bad-request'}
|
2006-08-04 03:57:51 +02:00
|
|
|
end;
|
2007-08-28 16:35:50 +02:00
|
|
|
set_xoption([{"muc#roomconfig_maxusers", [Val]} | Opts], Config) ->
|
|
|
|
case Val of
|
|
|
|
"none" ->
|
|
|
|
?SET_STRING_XOPT(max_users, none);
|
|
|
|
_ ->
|
2007-08-29 19:54:45 +02:00
|
|
|
?SET_NAT_XOPT(max_users, Val)
|
2007-08-28 16:35:50 +02:00
|
|
|
end;
|
2006-08-04 03:57:51 +02:00
|
|
|
set_xoption([{"muc#roomconfig_enablelogging", [Val]} | Opts], Config) ->
|
2003-03-25 22:03:35 +01:00
|
|
|
?SET_BOOL_XOPT(logging, Val);
|
2006-08-04 03:57:51 +02:00
|
|
|
set_xoption([{"FORM_TYPE", _} | Opts], Config) ->
|
|
|
|
%% Ignore our FORM_TYPE
|
|
|
|
set_xoption(Opts, Config);
|
2006-01-19 03:17:31 +01:00
|
|
|
set_xoption([_ | _Opts], _Config) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'bad-request'}.
|
2003-03-25 22:03:35 +01:00
|
|
|
|
|
|
|
|
|
|
|
change_config(Config, StateData) ->
|
|
|
|
NSD = StateData#state{config = Config},
|
|
|
|
case {(StateData#state.config)#config.persistent,
|
|
|
|
Config#config.persistent} of
|
|
|
|
{_, true} ->
|
2005-04-17 20:08:34 +02:00
|
|
|
mod_muc:store_room(NSD#state.host, NSD#state.room, make_opts(NSD));
|
2003-03-25 22:03:35 +01:00
|
|
|
{true, false} ->
|
2005-04-17 20:08:34 +02:00
|
|
|
mod_muc:forget_room(NSD#state.host, NSD#state.room);
|
2003-03-25 22:03:35 +01:00
|
|
|
{false, false} ->
|
|
|
|
ok
|
2006-02-06 06:12:54 +01:00
|
|
|
end,
|
|
|
|
case {(StateData#state.config)#config.members_only,
|
|
|
|
Config#config.members_only} of
|
|
|
|
{false, true} ->
|
|
|
|
NSD1 = remove_nonmembers(NSD),
|
|
|
|
{result, [], NSD1};
|
|
|
|
_ ->
|
|
|
|
{result, [], NSD}
|
|
|
|
end.
|
|
|
|
|
|
|
|
remove_nonmembers(StateData) ->
|
|
|
|
lists:foldl(
|
|
|
|
fun({_LJID, #user{jid = JID}}, SD) ->
|
|
|
|
Affiliation = get_affiliation(JID, SD),
|
|
|
|
case Affiliation of
|
|
|
|
none ->
|
|
|
|
catch send_kickban_presence(
|
|
|
|
JID, "", "322", SD),
|
|
|
|
set_role(JID, none, SD);
|
|
|
|
_ ->
|
|
|
|
SD
|
|
|
|
end
|
|
|
|
end, StateData, ?DICT:to_list(StateData#state.users)).
|
2003-03-25 22:03:35 +01:00
|
|
|
|
|
|
|
|
|
|
|
-define(CASE_CONFIG_OPT(Opt),
|
|
|
|
Opt -> StateData#state{
|
|
|
|
config = (StateData#state.config)#config{Opt = Val}}).
|
|
|
|
|
|
|
|
set_opts([], StateData) ->
|
|
|
|
StateData;
|
|
|
|
set_opts([{Opt, Val} | Opts], StateData) ->
|
|
|
|
NSD = case Opt of
|
2008-04-01 12:11:39 +02:00
|
|
|
title -> StateData#state{config = (StateData#state.config)#config{title = Val}};
|
2008-07-11 14:48:27 +02:00
|
|
|
description -> StateData#state{config = (StateData#state.config)#config{description = Val}};
|
2008-04-01 12:11:39 +02:00
|
|
|
allow_change_subj -> StateData#state{config = (StateData#state.config)#config{allow_change_subj = Val}};
|
|
|
|
allow_query_users -> StateData#state{config = (StateData#state.config)#config{allow_query_users = Val}};
|
|
|
|
allow_private_messages -> StateData#state{config = (StateData#state.config)#config{allow_private_messages = Val}};
|
2008-09-16 16:39:57 +02:00
|
|
|
allow_visitor_nickchange -> StateData#state{config = (StateData#state.config)#config{allow_visitor_nickchange = Val}};
|
|
|
|
allow_visitor_status -> StateData#state{config = (StateData#state.config)#config{allow_visitor_status = Val}};
|
2008-04-01 12:11:39 +02:00
|
|
|
public -> StateData#state{config = (StateData#state.config)#config{public = Val}};
|
|
|
|
public_list -> StateData#state{config = (StateData#state.config)#config{public_list = Val}};
|
|
|
|
persistent -> StateData#state{config = (StateData#state.config)#config{persistent = Val}};
|
|
|
|
moderated -> StateData#state{config = (StateData#state.config)#config{moderated = Val}};
|
|
|
|
members_by_default -> StateData#state{config = (StateData#state.config)#config{members_by_default = Val}};
|
|
|
|
members_only -> StateData#state{config = (StateData#state.config)#config{members_only = Val}};
|
|
|
|
allow_user_invites -> StateData#state{config = (StateData#state.config)#config{allow_user_invites = Val}};
|
|
|
|
password_protected -> StateData#state{config = (StateData#state.config)#config{password_protected = Val}};
|
2009-05-26 19:20:09 +02:00
|
|
|
captcha_protected -> StateData#state{config = (StateData#state.config)#config{captcha_protected = Val}};
|
2008-04-01 12:11:39 +02:00
|
|
|
password -> StateData#state{config = (StateData#state.config)#config{password = Val}};
|
|
|
|
anonymous -> StateData#state{config = (StateData#state.config)#config{anonymous = Val}};
|
|
|
|
logging -> StateData#state{config = (StateData#state.config)#config{logging = Val}};
|
2007-08-29 19:54:45 +02:00
|
|
|
max_users ->
|
|
|
|
ServiceMaxUsers = get_service_max_users(StateData),
|
|
|
|
MaxUsers = if
|
|
|
|
Val =< ServiceMaxUsers -> Val;
|
|
|
|
true -> ServiceMaxUsers
|
|
|
|
end,
|
|
|
|
StateData#state{
|
|
|
|
config = (StateData#state.config)#config{
|
|
|
|
max_users = MaxUsers}};
|
2003-03-25 22:03:35 +01:00
|
|
|
affiliations ->
|
|
|
|
StateData#state{affiliations = ?DICT:from_list(Val)};
|
2003-05-15 20:16:13 +02:00
|
|
|
subject ->
|
|
|
|
StateData#state{subject = Val};
|
|
|
|
subject_author ->
|
|
|
|
StateData#state{subject_author = Val};
|
2003-03-25 22:03:35 +01:00
|
|
|
_ -> StateData
|
|
|
|
end,
|
|
|
|
set_opts(Opts, NSD).
|
|
|
|
|
|
|
|
-define(MAKE_CONFIG_OPT(Opt), {Opt, Config#config.Opt}).
|
|
|
|
|
|
|
|
make_opts(StateData) ->
|
|
|
|
Config = StateData#state.config,
|
|
|
|
[
|
2003-03-27 21:55:09 +01:00
|
|
|
?MAKE_CONFIG_OPT(title),
|
2008-07-11 14:48:27 +02:00
|
|
|
?MAKE_CONFIG_OPT(description),
|
2003-03-25 22:03:35 +01:00
|
|
|
?MAKE_CONFIG_OPT(allow_change_subj),
|
|
|
|
?MAKE_CONFIG_OPT(allow_query_users),
|
|
|
|
?MAKE_CONFIG_OPT(allow_private_messages),
|
2008-09-16 16:39:57 +02:00
|
|
|
?MAKE_CONFIG_OPT(allow_visitor_status),
|
|
|
|
?MAKE_CONFIG_OPT(allow_visitor_nickchange),
|
2003-03-25 22:03:35 +01:00
|
|
|
?MAKE_CONFIG_OPT(public),
|
2003-05-15 20:16:13 +02:00
|
|
|
?MAKE_CONFIG_OPT(public_list),
|
2003-03-25 22:03:35 +01:00
|
|
|
?MAKE_CONFIG_OPT(persistent),
|
|
|
|
?MAKE_CONFIG_OPT(moderated),
|
|
|
|
?MAKE_CONFIG_OPT(members_by_default),
|
|
|
|
?MAKE_CONFIG_OPT(members_only),
|
|
|
|
?MAKE_CONFIG_OPT(allow_user_invites),
|
|
|
|
?MAKE_CONFIG_OPT(password_protected),
|
2009-05-26 19:20:09 +02:00
|
|
|
?MAKE_CONFIG_OPT(captcha_protected),
|
2003-08-15 21:17:12 +02:00
|
|
|
?MAKE_CONFIG_OPT(password),
|
2003-03-25 22:03:35 +01:00
|
|
|
?MAKE_CONFIG_OPT(anonymous),
|
|
|
|
?MAKE_CONFIG_OPT(logging),
|
2007-08-28 16:35:50 +02:00
|
|
|
?MAKE_CONFIG_OPT(max_users),
|
2003-05-15 20:16:13 +02:00
|
|
|
{affiliations, ?DICT:to_list(StateData#state.affiliations)},
|
|
|
|
{subject, StateData#state.subject},
|
|
|
|
{subject_author, StateData#state.subject_author}
|
2003-03-25 22:03:35 +01:00
|
|
|
].
|
|
|
|
|
2003-03-23 21:08:44 +01:00
|
|
|
|
2003-03-27 21:55:09 +01:00
|
|
|
|
2006-01-19 03:17:31 +01:00
|
|
|
destroy_room(DEl, StateData) ->
|
2003-03-27 21:55:09 +01:00
|
|
|
lists:foreach(
|
2006-01-19 03:17:31 +01:00
|
|
|
fun({_LJID, Info}) ->
|
2003-03-27 21:55:09 +01:00
|
|
|
Nick = Info#user.nick,
|
2009-01-21 14:34:26 +01:00
|
|
|
ItemAttrs = [?XMLATTR('affiliation', <<"none">>),
|
|
|
|
?XMLATTR('role', <<"none">>)],
|
2008-12-01 16:01:27 +01:00
|
|
|
Packet = #xmlel{ns = ?NS_JABBER_CLIENT,
|
|
|
|
name = 'presence',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('type',
|
|
|
|
<<"unavailable">>)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [
|
|
|
|
#xmlel{ns = ?NS_MUC_USER, name = 'x', children =
|
|
|
|
[#xmlel{name = 'item', attrs = ItemAttrs},
|
|
|
|
DEl]}]},
|
2003-03-27 21:55:09 +01:00
|
|
|
ejabberd_router:route(
|
2008-12-01 16:01:27 +01:00
|
|
|
jid_replace_resource(StateData#state.jid, Nick),
|
2003-03-27 21:55:09 +01:00
|
|
|
Info#user.jid,
|
|
|
|
Packet)
|
|
|
|
end, ?DICT:to_list(StateData#state.users)),
|
2004-10-08 22:40:29 +02:00
|
|
|
case (StateData#state.config)#config.persistent of
|
|
|
|
true ->
|
2005-04-17 20:08:34 +02:00
|
|
|
mod_muc:forget_room(StateData#state.host, StateData#state.room);
|
2004-10-08 22:40:29 +02:00
|
|
|
false ->
|
|
|
|
ok
|
|
|
|
end,
|
2003-06-30 14:24:35 +02:00
|
|
|
{result, [], stop}.
|
2003-03-27 21:55:09 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-03-27 15:06:17 +01:00
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
% Disco
|
|
|
|
|
2008-12-01 16:01:27 +01:00
|
|
|
-define(FEATURE(Var), #xmlel{name = 'feature',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('var', Var)]}).
|
2004-01-17 21:26:57 +01:00
|
|
|
|
|
|
|
-define(CONFIG_OPT_TO_FEATURE(Opt, Fiftrue, Fiffalse),
|
|
|
|
case Opt of
|
|
|
|
true ->
|
|
|
|
?FEATURE(Fiftrue);
|
|
|
|
false ->
|
|
|
|
?FEATURE(Fiffalse)
|
|
|
|
end).
|
|
|
|
|
2006-01-19 03:17:31 +01:00
|
|
|
process_iq_disco_info(_From, set, _Lang, _StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'not-allowed'};
|
2003-03-27 15:06:17 +01:00
|
|
|
|
2006-01-19 03:17:31 +01:00
|
|
|
process_iq_disco_info(_From, get, Lang, StateData) ->
|
2004-01-17 21:26:57 +01:00
|
|
|
Config = StateData#state.config,
|
2008-12-01 16:01:27 +01:00
|
|
|
{result, [ #xmlel{name = 'identity',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('category',
|
|
|
|
<<"conference">>),
|
|
|
|
?XMLATTR('type', <<"text">>),
|
|
|
|
?XMLATTR('name',
|
|
|
|
get_title(StateData))]},
|
2008-12-01 16:01:27 +01:00
|
|
|
#xmlel{name = 'feature',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('var', ?NS_MUC_s)]},
|
2008-12-01 16:01:27 +01:00
|
|
|
|
2004-01-17 21:26:57 +01:00
|
|
|
?CONFIG_OPT_TO_FEATURE(Config#config.public,
|
|
|
|
"muc_public", "muc_hidden"),
|
|
|
|
?CONFIG_OPT_TO_FEATURE(Config#config.persistent,
|
|
|
|
"muc_persistent", "muc_temporary"),
|
|
|
|
?CONFIG_OPT_TO_FEATURE(Config#config.members_only,
|
|
|
|
"muc_membersonly", "muc_open"),
|
|
|
|
?CONFIG_OPT_TO_FEATURE(Config#config.anonymous,
|
|
|
|
"muc_semianonymous", "muc_nonanonymous"),
|
|
|
|
?CONFIG_OPT_TO_FEATURE(Config#config.moderated,
|
|
|
|
"muc_moderated", "muc_unmoderated"),
|
|
|
|
?CONFIG_OPT_TO_FEATURE(Config#config.password_protected,
|
|
|
|
"muc_passwordprotected", "muc_unsecured")
|
2006-01-19 03:17:31 +01:00
|
|
|
] ++ iq_disco_info_extras(Lang, StateData), StateData}.
|
|
|
|
|
|
|
|
-define(RFIELDT(Type, Var, Val),
|
2009-01-21 14:34:26 +01:00
|
|
|
#xmlel{name = 'field', attrs = [?XMLATTR('type', Type),
|
|
|
|
?XMLATTR('var', Var)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{name = 'value',
|
|
|
|
children = [#xmlcdata{cdata = Val}]}]}).
|
2003-03-27 15:06:17 +01:00
|
|
|
|
2006-01-19 03:17:31 +01:00
|
|
|
-define(RFIELD(Label, Var, Val),
|
2009-01-21 14:34:26 +01:00
|
|
|
#xmlel{name = 'field', attrs = [?XMLATTR('label',
|
|
|
|
translate:translate(Lang, Label)),
|
|
|
|
?XMLATTR('var', Var)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{name = 'value', children = [
|
|
|
|
#xmlcdata{cdata = Val}]}]}).
|
2003-03-27 15:06:17 +01:00
|
|
|
|
2006-01-19 03:17:31 +01:00
|
|
|
iq_disco_info_extras(Lang, StateData) ->
|
|
|
|
Len = length(?DICT:to_list(StateData#state.users)),
|
2008-07-11 14:48:27 +02:00
|
|
|
RoomDescription = (StateData#state.config)#config.description,
|
2008-12-01 16:01:27 +01:00
|
|
|
[#xmlel{ns = ?NS_DATA_FORMS, name = 'x',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('type', <<"result">>)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children =
|
2006-01-19 03:17:31 +01:00
|
|
|
[?RFIELDT("hidden", "FORM_TYPE",
|
|
|
|
"http://jabber.org/protocol/muc#roominfo"),
|
2008-07-11 14:48:27 +02:00
|
|
|
?RFIELD("Room description", "muc#roominfo_description",
|
|
|
|
RoomDescription),
|
2006-01-19 03:17:31 +01:00
|
|
|
?RFIELD("Number of occupants", "muc#roominfo_occupants",
|
|
|
|
integer_to_list(Len))
|
|
|
|
]}].
|
|
|
|
|
|
|
|
process_iq_disco_items(_From, set, _Lang, _StateData) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'not-allowed'};
|
2003-03-27 15:06:17 +01:00
|
|
|
|
2006-01-19 03:17:31 +01:00
|
|
|
process_iq_disco_items(From, get, _Lang, StateData) ->
|
2003-03-27 15:06:17 +01:00
|
|
|
FAffiliation = get_affiliation(From, StateData),
|
|
|
|
FRole = get_role(From, StateData),
|
2003-05-15 20:16:13 +02:00
|
|
|
case ((StateData#state.config)#config.public_list == true) orelse
|
|
|
|
(FRole /= none) orelse
|
|
|
|
(FAffiliation == admin) orelse
|
2003-03-27 15:06:17 +01:00
|
|
|
(FAffiliation == owner) of
|
|
|
|
true ->
|
|
|
|
UList =
|
|
|
|
lists:map(
|
2006-01-19 03:17:31 +01:00
|
|
|
fun({_LJID, Info}) ->
|
2003-03-27 15:06:17 +01:00
|
|
|
Nick = Info#user.nick,
|
2009-01-21 14:34:26 +01:00
|
|
|
#xmlel{name = 'item', attrs = [?XMLATTR('jid',
|
2009-06-01 18:59:08 +02:00
|
|
|
exmpp_jid:to_binary(
|
2009-01-15 20:41:06 +01:00
|
|
|
StateData#state.room,
|
|
|
|
StateData#state.host,
|
2009-01-21 14:34:26 +01:00
|
|
|
Nick)),
|
|
|
|
?XMLATTR('name',
|
|
|
|
Nick)]}
|
2003-03-27 15:06:17 +01:00
|
|
|
end,
|
|
|
|
?DICT:to_list(StateData#state.users)),
|
|
|
|
{result, UList, StateData};
|
|
|
|
_ ->
|
2008-12-01 16:01:27 +01:00
|
|
|
{error, 'forbidden'}
|
2003-03-27 15:06:17 +01:00
|
|
|
end.
|
|
|
|
|
2009-05-26 19:20:09 +02:00
|
|
|
process_iq_captcha(_From, get, _Lang, _SubEl, _StateData) ->
|
|
|
|
{error, 'not-allowed'};
|
|
|
|
|
|
|
|
process_iq_captcha(_From, set, _Lang, SubEl, StateData) ->
|
|
|
|
case ejabberd_captcha:process_reply(SubEl) of
|
|
|
|
ok ->
|
|
|
|
{result, [], StateData};
|
|
|
|
_ ->
|
|
|
|
{error, 'not-acceptable'}
|
|
|
|
end.
|
|
|
|
|
2003-03-27 21:55:09 +01:00
|
|
|
get_title(StateData) ->
|
|
|
|
case (StateData#state.config)#config.title of
|
|
|
|
"" ->
|
2009-01-15 20:41:06 +01:00
|
|
|
binary_to_list(StateData#state.room);
|
2003-03-27 21:55:09 +01:00
|
|
|
Name ->
|
|
|
|
Name
|
|
|
|
end.
|
|
|
|
|
2003-03-27 15:06:17 +01:00
|
|
|
|
2003-04-13 21:22:46 +02:00
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
% Invitation support
|
|
|
|
|
2007-06-26 20:00:36 +02:00
|
|
|
check_invitation(From, Els, Lang, StateData) ->
|
2003-04-13 21:22:46 +02:00
|
|
|
FAffiliation = get_affiliation(From, StateData),
|
|
|
|
CanInvite = (StateData#state.config)#config.allow_user_invites
|
|
|
|
orelse (FAffiliation == admin) orelse (FAffiliation == owner),
|
2008-12-01 16:01:27 +01:00
|
|
|
InviteEl = case Els of
|
|
|
|
[#xmlel{ns = XMLNS, name = 'x'} = XEl] ->
|
|
|
|
case XMLNS of
|
|
|
|
?NS_MUC_USER -> ok;
|
|
|
|
_ -> throw({error, 'bad-request'})
|
|
|
|
end,
|
|
|
|
case exmpp_xml:get_child_elements(XEl) of
|
|
|
|
[#xmlel{name = 'invite'} = InviteEl1] ->
|
|
|
|
InviteEl1;
|
|
|
|
_ ->
|
|
|
|
throw({error, 'bad-request'})
|
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
throw({error, 'bad-request'})
|
|
|
|
end,
|
2009-06-01 18:35:55 +02:00
|
|
|
JID = try exmpp_jid:parse(exmpp_xml:get_attribute_as_binary(InviteEl,
|
2008-12-01 16:01:27 +01:00
|
|
|
'to',
|
|
|
|
false)) of
|
|
|
|
JID1 -> JID1
|
|
|
|
catch
|
|
|
|
_:_ ->
|
|
|
|
throw({error, 'jid-malformed'})
|
|
|
|
end,
|
2007-05-03 06:07:29 +02:00
|
|
|
case CanInvite of
|
|
|
|
false ->
|
2008-12-01 16:01:27 +01:00
|
|
|
throw({error, 'not-allowed'});
|
2007-05-03 06:07:29 +02:00
|
|
|
true ->
|
|
|
|
Reason =
|
2008-12-01 16:01:27 +01:00
|
|
|
exmpp_xml:get_path(
|
2007-05-03 06:07:29 +02:00
|
|
|
InviteEl,
|
2008-12-01 16:01:27 +01:00
|
|
|
[{element, 'reason'}, cdata]),
|
2008-01-01 18:06:26 +01:00
|
|
|
ContinueEl =
|
2008-12-17 17:24:15 +01:00
|
|
|
case exmpp_xml:get_path(
|
2008-01-01 18:06:26 +01:00
|
|
|
InviteEl,
|
2008-12-01 16:01:27 +01:00
|
|
|
[{element, 'continue'}]) of
|
2008-12-17 17:24:15 +01:00
|
|
|
'undefined' -> [];
|
2008-01-01 18:06:26 +01:00
|
|
|
Continue1 -> [Continue1]
|
|
|
|
end,
|
2007-05-03 06:07:29 +02:00
|
|
|
IEl =
|
2008-12-17 17:24:15 +01:00
|
|
|
[#xmlel{ns = ?NS_MUC_USER,
|
|
|
|
name = 'invite',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('from',
|
2009-06-01 18:59:08 +02:00
|
|
|
exmpp_jid:to_binary(From))],
|
2008-12-17 17:24:15 +01:00
|
|
|
children = [#xmlel{ns =?NS_MUC_USER, name = 'reason',
|
|
|
|
children = [#xmlcdata{cdata = Reason} ]}]
|
|
|
|
++ ContinueEl}],
|
2007-08-28 16:35:50 +02:00
|
|
|
PasswdEl =
|
2007-05-03 06:07:29 +02:00
|
|
|
case (StateData#state.config)#config.password_protected of
|
|
|
|
true ->
|
2008-12-17 17:24:15 +01:00
|
|
|
[#xmlel{ns = ?NS_MUC_USER, name = 'password',
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlcdata{cdata =
|
|
|
|
(StateData#state.config)#config.password}]}];
|
2007-05-03 06:07:29 +02:00
|
|
|
_ ->
|
|
|
|
[]
|
|
|
|
end,
|
2007-06-26 20:00:36 +02:00
|
|
|
Body =
|
2008-12-01 16:01:27 +01:00
|
|
|
#xmlel{name = 'body',
|
|
|
|
children = [#xmlcdata{cdata =
|
2008-12-17 17:24:15 +01:00
|
|
|
list_to_binary([
|
2008-12-01 16:01:27 +01:00
|
|
|
io_lib:format(
|
|
|
|
translate:translate(Lang,
|
|
|
|
"~s invites you to the room ~s"),
|
2009-06-01 18:59:08 +02:00
|
|
|
[exmpp_jid:to_binary(From),
|
|
|
|
exmpp_jid:to_binary(StateData#state.room,
|
2009-01-15 20:41:06 +01:00
|
|
|
StateData#state.host)
|
2008-12-17 17:24:15 +01:00
|
|
|
]),
|
2007-06-26 20:00:36 +02:00
|
|
|
case (StateData#state.config)#config.password_protected of
|
|
|
|
true ->
|
|
|
|
", " ++
|
|
|
|
translate:translate(Lang, "the password is") ++
|
|
|
|
" '" ++
|
|
|
|
(StateData#state.config)#config.password ++ "'";
|
|
|
|
_ ->
|
|
|
|
""
|
2008-12-17 17:24:15 +01:00
|
|
|
end,
|
2007-06-26 20:00:36 +02:00
|
|
|
case Reason of
|
2008-12-17 17:24:15 +01:00
|
|
|
<<>> -> "";
|
|
|
|
_ -> [" (", Reason, ") "]
|
2007-06-26 20:00:36 +02:00
|
|
|
end
|
2008-12-17 17:24:15 +01:00
|
|
|
])}]},
|
2009-01-03 16:15:38 +01:00
|
|
|
%%TODO: always NS_JABBER_CLIENT?
|
2007-05-03 06:07:29 +02:00
|
|
|
Msg =
|
2008-12-17 17:24:15 +01:00
|
|
|
#xmlel{ns = ?NS_JABBER_CLIENT, name = 'message',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('type', <<"normal">>)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlel{ns = ?NS_MUC_USER, name = 'x',
|
|
|
|
children = IEl ++ PasswdEl},
|
|
|
|
#xmlel{ns = 'jabber:x:conference', name = 'x',
|
2009-01-21 14:34:26 +01:00
|
|
|
attrs = [?XMLATTR('jid',
|
2009-06-01 18:59:08 +02:00
|
|
|
exmpp_jid:to_binary(
|
2008-12-01 16:01:27 +01:00
|
|
|
StateData#state.room,
|
2009-01-15 20:41:06 +01:00
|
|
|
StateData#state.host)
|
2009-01-21 14:34:26 +01:00
|
|
|
)],
|
2008-12-01 16:01:27 +01:00
|
|
|
children = [#xmlcdata{cdata = Reason}]},
|
|
|
|
Body]},
|
2007-05-03 06:07:29 +02:00
|
|
|
ejabberd_router:route(StateData#state.jid, JID, Msg),
|
|
|
|
JID
|
2003-04-13 21:22:46 +02:00
|
|
|
end.
|
|
|
|
|
2008-02-06 21:30:58 +01:00
|
|
|
%% Handle a message sent to the room by a non-participant.
|
|
|
|
%% If it is a decline, send to the inviter.
|
|
|
|
%% Otherwise, an error message is sent to the sender.
|
|
|
|
handle_roommessage_from_nonparticipant(Packet, Lang, StateData, From) ->
|
|
|
|
case catch check_decline_invitation(Packet) of
|
|
|
|
{true, Decline_data} ->
|
2008-02-11 13:15:34 +01:00
|
|
|
send_decline_invitation(Decline_data, StateData#state.jid, From);
|
2008-02-06 21:30:58 +01:00
|
|
|
_ ->
|
|
|
|
send_error_only_occupants(Packet, Lang, StateData#state.jid, From)
|
|
|
|
end.
|
|
|
|
|
|
|
|
%% Check in the packet is a decline.
|
|
|
|
%% If so, also returns the splitted packet.
|
|
|
|
%% This function must be catched,
|
|
|
|
%% because it crashes when the packet is not a decline message.
|
|
|
|
check_decline_invitation(Packet) ->
|
2008-12-01 16:01:27 +01:00
|
|
|
#xmlel{name = 'message'} = Packet,
|
|
|
|
#xmlel{ns = ?NS_MUC_USER} = XEl = exmpp_xml:get_element(Packet, 'x'),
|
|
|
|
DEl = exmpp_xml:get_element(XEl, 'decline'),
|
2009-01-09 20:18:46 +01:00
|
|
|
ToString = exmpp_xml:get_attribute_as_binary(DEl, 'to', false),
|
2009-06-01 18:35:55 +02:00
|
|
|
ToJID = exmpp_jid:parse(ToString),
|
2008-02-11 13:15:34 +01:00
|
|
|
{true, {Packet, XEl, DEl, ToJID}}.
|
2008-02-06 21:30:58 +01:00
|
|
|
|
|
|
|
%% Send the decline to the inviter user.
|
|
|
|
%% The original stanza must be slightly modified.
|
2008-12-01 16:01:27 +01:00
|
|
|
send_decline_invitation({Packet, XEl, DEl = #xmlel{name='decline'}, ToJID},
|
|
|
|
RoomJID, FromJID) ->
|
2009-06-01 18:59:08 +02:00
|
|
|
FromString = exmpp_jid:to_binary(FromJID),
|
2008-12-01 16:01:27 +01:00
|
|
|
|
|
|
|
DEl1 = exmpp_xml:remove_attribute(DEl, 'to'),
|
|
|
|
DEl2 = exmpp_xml:set_attribute(DEl1, 'from',FromString),
|
|
|
|
XEl2 = replace_subelement(XEl,DEl2),
|
|
|
|
Packet2 = replace_subelement(Packet,XEl2),
|
2008-02-06 21:30:58 +01:00
|
|
|
ejabberd_router:route(RoomJID, ToJID, Packet2).
|
|
|
|
|
|
|
|
%% Given an element and a new subelement,
|
|
|
|
%% replace the instance of the subelement in element with the new subelement.
|
2008-12-01 16:01:27 +01:00
|
|
|
replace_subelement(#xmlel{children = Els} = El, #xmlel{name = Name} = NewSubEl) ->
|
|
|
|
Els2 = lists:map(fun(#xmlel{name = Name2}) when Name2 =:= Name -> NewSubEl;
|
|
|
|
(S) -> S
|
|
|
|
end, Els),
|
|
|
|
exmpp_xml:set_children(El, Els2).
|
2008-02-06 21:30:58 +01:00
|
|
|
|
|
|
|
send_error_only_occupants(Packet, Lang, RoomJID, From) ->
|
|
|
|
ErrText = "Only occupants are allowed to send messages to the conference",
|
2008-12-01 16:01:27 +01:00
|
|
|
Err = exmpp_stanza:reply_with_error(
|
|
|
|
Packet, ?ERR(Packet, 'not-acceptable', Lang, ErrText)),
|
2008-02-06 21:30:58 +01:00
|
|
|
ejabberd_router:route(RoomJID, From, Err).
|
|
|
|
|
2006-03-14 05:26:15 +01:00
|
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
% Logging
|
|
|
|
|
2009-07-21 20:35:20 +02:00
|
|
|
add_to_log(Type, Data, StateData)
|
|
|
|
when Type == roomconfig_change_disabledlogging ->
|
|
|
|
%% When logging is disabled, the config change message must be logged:
|
|
|
|
mod_muc_log:add_to_log(
|
|
|
|
StateData#state.server_host, roomconfig_change, Data,
|
|
|
|
StateData#state.jid, make_opts(StateData));
|
2006-03-14 05:26:15 +01:00
|
|
|
add_to_log(Type, Data, StateData) ->
|
|
|
|
case (StateData#state.config)#config.logging of
|
|
|
|
true ->
|
|
|
|
mod_muc_log:add_to_log(
|
|
|
|
StateData#state.server_host, Type, Data,
|
|
|
|
StateData#state.jid, make_opts(StateData));
|
|
|
|
false ->
|
|
|
|
ok
|
|
|
|
end.
|
2007-12-03 11:47:42 +01:00
|
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
%% Users number checking
|
|
|
|
|
|
|
|
tab_add_online_user(JID, StateData) ->
|
2009-06-01 18:39:36 +02:00
|
|
|
LUser = exmpp_jid:prep_node(JID),
|
2009-06-01 18:37:15 +02:00
|
|
|
LServer = exmpp_jid:prep_domain(JID),
|
2007-12-03 11:47:42 +01:00
|
|
|
US = {LUser, LServer},
|
|
|
|
Room = StateData#state.room,
|
|
|
|
Host = StateData#state.host,
|
|
|
|
catch ets:insert(
|
|
|
|
muc_online_users,
|
|
|
|
#muc_online_users{us = US, room = Room, host = Host}).
|
|
|
|
|
|
|
|
|
2008-12-01 16:01:27 +01:00
|
|
|
|
2009-01-03 16:15:38 +01:00
|
|
|
tab_remove_online_user(JID, StateData) when ?IS_JID(JID) ->
|
2009-06-01 18:39:36 +02:00
|
|
|
LUser = exmpp_jid:prep_node(JID),
|
2009-06-01 18:37:15 +02:00
|
|
|
LServer = exmpp_jid:prep_domain(JID),
|
2008-12-01 16:01:27 +01:00
|
|
|
tab_remove_online_user({LUser, LServer, none},StateData);
|
|
|
|
|
|
|
|
tab_remove_online_user({LUser, LServer,_}, StateData) ->
|
2007-12-03 11:47:42 +01:00
|
|
|
US = {LUser, LServer},
|
|
|
|
Room = StateData#state.room,
|
|
|
|
Host = StateData#state.host,
|
|
|
|
catch ets:delete_object(
|
|
|
|
muc_online_users,
|
|
|
|
#muc_online_users{us = US, room = Room, host = Host}).
|
|
|
|
|
|
|
|
tab_count_user(JID) ->
|
2009-06-01 18:39:36 +02:00
|
|
|
LUser = exmpp_jid:prep_node(JID),
|
2009-06-01 18:37:15 +02:00
|
|
|
LServer = exmpp_jid:prep_domain(JID),
|
2007-12-03 11:47:42 +01:00
|
|
|
US = {LUser, LServer},
|
|
|
|
case catch ets:select(
|
|
|
|
muc_online_users,
|
|
|
|
[{#muc_online_users{us = US, _ = '_'}, [], [[]]}]) of
|
|
|
|
Res when is_list(Res) ->
|
|
|
|
length(Res);
|
|
|
|
_ ->
|
|
|
|
0
|
|
|
|
end.
|
2008-12-01 16:01:27 +01:00
|
|
|
|
|
|
|
|
|
|
|
jid_replace_resource(JID, Resource) ->
|
2009-06-01 18:34:38 +02:00
|
|
|
exmpp_jid:full(JID, Resource).
|
2008-12-01 16:01:27 +01:00
|
|
|
|
|
|
|
|
|
|
|
|