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>
%%%
%%%
2011-02-14 13:50:55 +01:00
%%% ejabberd, Copyright (C) 2002-2011 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-12 15:44:42 +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 ] ) .
- include ( " ejabberd.hrl " ) .
- include ( " jlib.hrl " ) .
2008-12-02 20:23:12 +01:00
- include ( " mod_muc_room.hrl " ) .
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
%%%----------------------------------------------------------------------
%%% 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
%%----------------------------------------------------------------------
2007-09-01 23:05:04 +02: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 ) ,
2004-05-01 22:10:25 +02:00
jid = jlib : make_jid ( 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 " ,
[ Room , Host , jlib : jid_to_string ( Creator ) ] ) ,
2009-12-29 15:43:24 +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 } ;
2007-09-01 23:05:04 +02: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 ) ,
2007-09-01 23:05:04 +02:00
jid = jlib : make_jid ( Room , Host , " " ) ,
room_shaper = Shaper } ) ,
2009-12-29 15:43:24 +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
%%----------------------------------------------------------------------
normal_state ( { route , From , " " ,
{ xmlelement , " message " , Attrs , Els } = Packet } ,
StateData ) - >
2004-03-02 22:16:55 +01:00
Lang = xml : get_attr_s ( " xml:lang " , Attrs ) ,
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 - >
case xml : get_attr_s ( " type " , Attrs ) 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 ) ,
2010-07-01 12:54:01 +02:00
Size = element_size ( 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 " ,
Err = jlib : make_error_reply (
Packet , ? ERRT_RESOURCE_CONSTRAINT ( Lang , ErrText ) ) ,
ejabberd_router : route (
StateData #state.jid ,
From , Err ) ,
{ 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-11-28 17:06:39 +01:00
store_user_activity (
From , NewActivity , StateData ) ,
StateData2 =
StateData1 #state {
2007-09-01 23:05:04 +02:00
room_shaper = RoomShaper } ,
2008-11-28 17:06:39 +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-11-28 17:06:39 +01:00
store_user_activity (
From , NewActivity , StateData1 ) ,
StateData3 =
StateData2 #state {
2007-09-01 23:05:04 +02:00
room_queue = RoomQueue } ,
2008-11-28 17:06:39 +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-11-28 17:06:39 +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 ;
" 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 ;
2004-03-02 22:16:55 +01:00
" chat " - >
ErrText = " It is not allowed to send private messages to the conference " ,
Err = jlib : make_error_reply (
Packet , ? ERRT_NOT_ACCEPTABLE ( Lang , ErrText ) ) ,
ejabberd_router : route (
StateData #state.jid ,
From , Err ) ,
{ next_state , normal_state , StateData } ;
2003-04-13 21:22:46 +02:00
Type when ( Type == " " ) or ( Type == " normal " ) - >
2011-07-23 11:16:17 +02:00
IsInvitation = is_invitation ( Els ) ,
2011-07-23 16:13:27 +02:00
IsVoiceRequest = is_voice_request ( Els ) ,
2011-07-25 18:09:11 +02:00
IsVoiceApprovement = is_voice_approvement ( Els ) ,
2011-07-23 11:16:17 +02:00
if
IsInvitation - >
case catch check_invitation ( From , Els , Lang , StateData ) of
{ error , Error } - >
Err = jlib : make_error_reply (
Packet , Error ) ,
ejabberd_router : route (
StateData #state.jid ,
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 ) ,
case ( NSD #state.config ) #config.persistent of
true - >
mod_muc : store_room (
NSD #state.host ,
NSD #state.room ,
make_opts ( NSD ) ) ;
_ - >
ok
end ,
{ next_state , normal_state , NSD } ;
2005-05-09 21:38:16 +02:00
_ - >
2011-07-23 11:16:17 +02:00
{ next_state , normal_state ,
StateData }
end ;
false - >
{ next_state , normal_state , StateData }
end
end ;
2011-07-23 16:13:27 +02:00
IsVoiceRequest - >
2011-07-25 18:54:45 +02:00
case is_visitor ( From , StateData ) of
true - >
send_voice_request ( From , StateData ) ;
_ - >
ErrText = " Only visitors allowed to request voice " ,
Err = jlib : make_error_reply (
Packet , ? ERRT_NOT_ALLOWED ( Lang , ErrText ) ) ,
ejabberd_router : route (
StateData #state.jid , From , Err )
end ,
2011-07-23 16:13:27 +02:00
{ next_state , normal_state , StateData } ;
2011-07-25 18:09:11 +02:00
IsVoiceApprovement - >
2011-07-25 21:46:59 +02:00
NewStateData = case is_moderator ( From , StateData ) of
true - >
case extract_jid_from_voice_approvement ( Els ) of
{ error , X } - >
? ERROR_MSG ( " Failed to extract JID from voice approvement: ~n ~p " , [ X ] ) ,
ErrText = " Failed to extract JID from your voice request approvement " ,
Err = jlib : make_error_reply (
Packet , ? ERRT_BAD_REQUEST ( Lang , ErrText ) ) ,
ejabberd_router : route (
StateData #state.jid , From , Err ) ,
StateData ;
TargetJid - >
case is_visitor ( TargetJid , StateData ) of
true - >
Reason = [ ] ,
NSD = set_role ( TargetJid , participant , StateData ) ,
catch send_new_presence ( TargetJid , Reason , NSD ) ,
NSD ;
_ - >
StateData
end
end ;
_ - >
ErrText = " Only moderators can approve voice requests " ,
Err = jlib : make_error_reply (
Packet , ? ERRT_NOT_ALLOWED ( Lang , ErrText ) ) ,
ejabberd_router : route (
StateData #state.jid , From , Err ) ,
StateData
end ,
{ next_state , normal_state , NewStateData } ;
2011-07-23 11:16:17 +02:00
true - >
{ next_state , normal_state , StateData }
end ;
2003-03-23 21:08:44 +01:00
_ - >
2004-03-02 22:16:55 +01:00
ErrText = " Improper message type " ,
2003-03-23 21:08:44 +01:00
Err = jlib : make_error_reply (
2004-03-02 22:16:55 +01:00
Packet , ? ERRT_NOT_ACCEPTABLE ( Lang , ErrText ) ) ,
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
From , Err ) ,
{ next_state , normal_state , StateData }
end ;
_ - >
2003-11-10 15:32:19 +01:00
case xml : get_attr_s ( " type " , Attrs ) of
" error " - >
ok ;
_ - >
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 ;
normal_state ( { route , From , " " ,
2004-05-17 22:36:41 +02:00
{ xmlelement , " iq " , _ Attrs , _ Els } = Packet } ,
2003-03-23 21:08:44 +01:00
StateData ) - >
case jlib : iq_query_info ( Packet ) of
2004-02-26 23:00:04 +01:00
#iq { type = Type , xmlns = XMLNS , lang = Lang , sub_el = 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-03-13 17:01:46 +01: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-03-13 17:01:46 +01: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
2003-03-23 21:08:44 +01:00
{ result , Res , SD } - >
2003-12-17 21:13:21 +01:00
{ IQ #iq { type = result ,
sub_el = [ { xmlelement , " query " ,
[ { " xmlns " , XMLNS } ] ,
Res
} ] } ,
2003-03-23 21:08:44 +01:00
SD } ;
{ error , Error } - >
2003-12-17 21:13:21 +01:00
{ IQ #iq { type = error ,
sub_el = [ SubEl , Error ] } ,
2003-03-23 21:08:44 +01:00
StateData }
end ,
2003-10-07 22:31:44 +02:00
ejabberd_router : route ( StateData #state.jid ,
2003-03-23 21:08:44 +01:00
From ,
jlib : iq_to_xml ( 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
_ - >
Err = jlib : make_error_reply (
Packet , ? ERR_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 ,
2007-08-02 04:30:25 +02:00
{ xmlelement , " presence " , _ Attrs , _ Els } = 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-11-28 17:06:39 +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-11-28 17:06:39 +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-01-29 15:49:08 +01:00
{ xmlelement , " message " , Attrs , _ } = Packet } ,
2003-03-23 21:08:44 +01:00
StateData ) - >
2004-01-17 21:26:57 +01:00
Type = xml : get_attr_s ( " type " , Attrs ) ,
2004-03-02 22:16:55 +01:00
Lang = xml : get_attr_s ( " xml:lang " , Attrs ) ,
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 \" " ,
2003-05-14 21:58:05 +02:00
Err = jlib : make_error_reply (
2008-01-29 15:49:08 +01:00
Packet , ? ERRT_BAD_REQUEST ( Lang , ErrText ) ) ,
2003-05-14 21:58:05 +02:00
ejabberd_router : route (
2008-01-09 17:01:16 +01:00
jlib : jid_replace_resource (
StateData #state.jid ,
ToNick ) ,
From , Err ) ;
2008-01-29 15:49:08 +01:00
_ - >
2011-09-04 13:28:32 +02:00
case find_jids_by_nick ( ToNick , StateData ) of
false - >
ErrText = " Recipient is not in the conference room " ,
2008-01-29 15:49:08 +01:00
Err = jlib : make_error_reply (
2011-09-04 13:28:32 +02:00
Packet , ? ERRT_ITEM_NOT_FOUND ( Lang , ErrText ) ) ,
2008-01-29 15:49:08 +01:00
ejabberd_router : route (
jlib : jid_replace_resource (
StateData #state.jid ,
ToNick ) ,
2011-09-04 13:28:32 +02:00
From , Err ) ;
ToJIDs - >
SrcIsVisitor = is_visitor ( From , StateData ) ,
DstIsModerator = is_moderator ( hd ( ToJIDs ) , StateData ) ,
PmFromVisitors = ( StateData #state.config ) #config.allow_private_messages_from_visitors ,
if SrcIsVisitor == false ;
PmFromVisitors == anyone ;
( PmFromVisitors == moderators ) and ( DstIsModerator ) - >
{ ok , #user { nick = FromNick } } =
? DICT : find ( jlib : jid_tolower ( From ) ,
StateData #state.users ) ,
FromNickJID = jlib : jid_replace_resource ( StateData #state.jid , FromNick ) ,
[ ejabberd_router : route ( FromNickJID , ToJID , Packet ) | | ToJID < - ToJIDs ] ;
true - >
ErrText = " It is not allowed to send private messages " ,
Err = jlib : make_error_reply (
Packet , ? ERRT_FORBIDDEN ( Lang , ErrText ) ) ,
ejabberd_router : route (
jlib : jid_replace_resource (
StateData #state.jid ,
ToNick ) ,
From , Err )
end
2008-01-29 15:49:08 +01:00
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 " ,
Err = jlib : make_error_reply (
Packet , ? ERRT_NOT_ACCEPTABLE ( Lang , ErrText ) ) ,
2008-05-12 22:35:43 +02:00
ejabberd_router : route (
jlib : jid_replace_resource (
StateData #state.jid ,
ToNick ) ,
From , Err ) ;
{ false , _ } - >
ErrText = " It is not allowed to send private messages " ,
Err = jlib : make_error_reply (
Packet , ? ERRT_FORBIDDEN ( Lang , ErrText ) ) ,
2008-01-29 15:49:08 +01:00
ejabberd_router : route (
jlib : jid_replace_resource (
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 ,
2004-05-17 22:36:41 +02:00
{ xmlelement , " iq " , Attrs , _ Els } = Packet } ,
2003-03-23 21:08:44 +01:00
StateData ) - >
2004-03-02 22:16:55 +01:00
Lang = xml : get_attr_s ( " xml:lang " , Attrs ) ,
2009-12-01 21:00:15 +01:00
StanzaId = xml : get_attr_s ( " id " , Attrs ) ,
2004-03-02 22:16:55 +01:00
case { ( StateData #state.config ) #config.allow_query_users ,
2009-12-01 21:00:15 +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 - >
2003-11-23 21:11:21 +01:00
case jlib : iq_query_info ( Packet ) of
reply - >
ok ;
_ - >
2004-03-02 22:16:55 +01:00
ErrText = " Recipient is not in the conference room " ,
2003-11-23 21:11:21 +01:00
Err = jlib : make_error_reply (
2004-03-02 22:16:55 +01:00
Packet , ? ERRT_ITEM_NOT_FOUND ( Lang , ErrText ) ) ,
2003-11-23 21:11:21 +01:00
ejabberd_router : route (
jlib : jid_replace_resource (
StateData #state.jid , ToNick ) ,
From , Err )
end ;
2003-03-25 22:03:35 +01:00
ToJID - >
{ ok , #user { nick = FromNick } } =
2009-12-01 21:00:15 +01:00
? DICT : find ( jlib : jid_tolower ( FromFull ) ,
2003-03-25 22:03:35 +01:00
StateData #state.users ) ,
2009-12-01 21:00:15 +01:00
{ ToJID2 , Packet2 } = handle_iq_vcard ( FromFull , ToJID ,
StanzaId , NewId , Packet ) ,
2003-03-25 22:03:35 +01:00
ejabberd_router : route (
2003-10-07 22:31:44 +02:00
jlib : jid_replace_resource ( StateData #state.jid , FromNick ) ,
2009-12-01 21:00:15 +01:00
ToJID2 , Packet2 )
2003-03-25 22:03:35 +01:00
end ;
2009-12-01 21:00:15 +01:00
{ _ , { false , _ , _ } } - >
2004-03-02 22:16:55 +01:00
case jlib : iq_query_info ( Packet ) of
reply - >
ok ;
_ - >
ErrText = " Only occupants are allowed to send queries to the conference " ,
Err = jlib : make_error_reply (
Packet , ? ERRT_NOT_ACCEPTABLE ( Lang , ErrText ) ) ,
ejabberd_router : route (
jlib : jid_replace_resource ( StateData #state.jid , ToNick ) ,
From , Err )
end ;
2003-03-25 22:03:35 +01:00
_ - >
2003-11-23 21:11:21 +01:00
case jlib : iq_query_info ( Packet ) of
reply - >
ok ;
_ - >
2004-03-02 22:16:55 +01:00
ErrText = " Queries to the conference members are not allowed in this room " ,
2003-11-23 21:11:21 +01:00
Err = jlib : make_error_reply (
2004-03-02 22:16:55 +01:00
Packet , ? ERRT_NOT_ALLOWED ( Lang , ErrText ) ) ,
2003-11-23 21:11:21 +01:00
ejabberd_router : route (
jlib : jid_replace_resource ( StateData #state.jid , ToNick ) ,
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 ) - >
2003-05-18 18:41:15 +02:00
MessagePkt = { xmlelement , " message " ,
[ { " type " , " groupchat " } ] ,
[ { xmlelement , " body " , [ ] , [ { xmlcdata , Msg } ] } ] } ,
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:51:25 +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 } =
destroy_room (
{ xmlelement , " destroy " ,
[ { " xmlns " , ? NS_MUC_OWNER } ] ,
case Reason of
none - > [ ] ;
_ Else - >
[ { xmlelement , " reason " ,
[ ] , [ { xmlcdata , Reason } ] } ]
end } , StateData ) ,
2008-01-16 11:08:17 +01:00
? INFO_MSG ( " Destroyed MUC room ~s with reason: ~p " ,
[ jlib : jid_to_string ( StateData #state.jid ) , Reason ] ) ,
2009-12-29 15:43:24 +01:00
add_to_log ( room_existence , destroyed , StateData ) ,
2010-02-02 12:49:49 +01:00
{ stop , shutdown , 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 " ,
[ jlib : jid_to_string ( 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 ) - >
2010-06-23 09:44:35 +02:00
Reply = get_roomdesc_reply ( JID , StateData ,
get_roomdesc_tail ( StateData , Lang ) ) ,
{ 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-11-28 17:06:39 +01:00
store_user_activity (
From , NewActivity , StateData ) ,
StateData2 =
StateData1 #state {
2007-09-01 23:05:04 +02:00
room_queue = RoomQueue } ,
2008-11-28 17:06:39 +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-11-28 17:06:39 +01:00
store_user_activity (
From , NewActivity , StateData ) ,
StateData2 =
StateData1 #state {
2007-09-01 23:05:04 +02:00
room_queue = RoomQueue } ,
2008-11-28 17:06:39 +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-03-13 17:01:46 +01: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 = jlib : make_error_reply (
Packet , ? ERR_NOT_AUTHORIZED ) ,
ejabberd_router : route ( % TODO: s/Nick/""/
jlib : 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:25:35 +01:00
terminate ( Reason , _ StateName , StateData ) - >
2009-12-29 15:43:24 +01:00
? INFO_MSG ( " Stopping MUC room ~s @ ~s " ,
[ StateData #state.room , StateData #state.host ] ) ,
2009-12-30 02:25:35 +01:00
ReasonT = case Reason of
shutdown - > " You are being removed from the room because "
" of a system shutdown " ;
2010-02-02 12:14:48 +01:00
_ - > " Room terminates "
2009-12-30 02:25:35 +01:00
end ,
ItemAttrs = [ { " affiliation " , " none " } , { " role " , " none " } ] ,
ReasonEl = { xmlelement , " reason " , [ ] , [ { xmlcdata , ReasonT } ] } ,
Packet = { xmlelement , " presence " , [ { " type " , " unavailable " } ] ,
[ { xmlelement , " x " , [ { " xmlns " , ? NS_MUC_USER } ] ,
[ { xmlelement , " item " , ItemAttrs , [ ReasonEl ] } ,
{ xmlelement , " status " , [ { " code " , " 332 " } ] , [ ] }
] } ] } ,
2007-12-03 11:47:42 +01:00
? DICT : fold (
2009-12-30 02:25:35 +01:00
fun ( LJID , Info , _ ) - >
Nick = Info #user.nick ,
case Reason of
shutdown - >
ejabberd_router : route (
jlib : jid_replace_resource ( 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:43:24 +01:00
add_to_log ( room_existence , stopped , StateData ) ,
2007-01-19 05:46:44 +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 } ) .
2007-06-25 18:43:42 +02:00
process_groupchat_message ( From , { xmlelement , " message " , Attrs , _ Els } = Packet ,
StateData ) - >
Lang = xml : get_attr_s ( " xml:lang " , Attrs ) ,
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 (
jlib : jid_replace_resource (
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:51:25 +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 - >
2007-09-01 23:05:04 +02:00
? ERRT_FORBIDDEN (
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
_ - >
2007-09-01 23:05:04 +02:00
? ERRT_FORBIDDEN (
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 ,
jlib : make_error_reply ( Packet , Err ) ) ,
{ 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 " ,
Err = jlib : make_error_reply (
Packet , ? ERRT_FORBIDDEN ( 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 " ,
2007-06-25 18:43:42 +02:00
Err = jlib : make_error_reply (
2007-09-01 23:05:04 +02:00
Packet , ? ERRT_NOT_ACCEPTABLE ( Lang , ErrText ) ) ,
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.
is_user_allowed_message_nonparticipant ( JID , StateData ) - >
2008-07-17 19:32:11 +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 ) - >
case ? DICT : find ( jlib : jid_tolower ( From ) , StateData #state.users ) of
{ ok , #user { nick = FromNick , role = Role } } - >
{ FromNick , Role } ;
error - >
{ " " , moderator }
end .
2007-06-25 18:43:42 +02:00
process_presence ( From , Nick , { xmlelement , " presence " , Attrs , _ Els } = Packet ,
StateData ) - >
Type = xml : get_attr_s ( " type " , Attrs ) ,
Lang = xml : get_attr_s ( " xml:lang " , Attrs ) ,
StateData1 =
case Type of
" unavailable " - >
case is_user_online ( From , StateData ) of
true - >
2010-11-15 05:47:31 +01:00
NewPacket = case { ( StateData #state.config ) #config.allow_visitor_status ,
is_visitor ( From , StateData ) } of
{ false , true } - >
strip_status ( Packet ) ;
_ - >
Packet
end ,
2007-06-25 18:43:42 +02:00
NewState =
2010-11-15 05:47:31 +01:00
add_user_presence_un ( From , NewPacket , StateData ) ,
2011-08-23 21:53:19 +02:00
case ? DICT : find ( Nick , StateData #state.nicks ) of
{ ok , [ _ , _ | _ ] } - > ok ;
_ - > send_new_presence ( From , NewState )
end ,
2010-11-15 05:47:31 +01:00
Reason = case xml : get_subtag ( NewPacket , " status " ) of
2007-06-25 18:43:42 +02:00
false - > " " ;
Status_el - > xml : get_tag_cdata ( Status_el )
end ,
remove_online_user ( From , NewState , Reason ) ;
_ - >
StateData
end ;
" error " - >
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 ;
" " - >
case is_user_online ( From , StateData ) of
true - >
case is_nick_change ( From , Nick , StateData ) of
true - >
2009-08-21 15:22:18 +02:00
case { nick_collision ( From , Nick , StateData ) ,
2007-06-25 18:43:42 +02:00
mod_muc : can_use_nick (
2008-07-23 14:31:55 +02:00
StateData #state.host , From , Nick ) ,
{ ( StateData #state.config ) #config.allow_visitor_nickchange ,
is_visitor ( From , StateData ) } } of
{ _ , _ , { false , true } } - >
ErrText = " Visitors are not allowed to change their nicknames in this room " ,
Err = jlib : make_error_reply (
Packet ,
? ERRT_NOT_ALLOWED ( Lang , ErrText ) ) ,
ejabberd_router : route (
% TODO: s/Nick/""/
jlib : jid_replace_resource (
StateData #state.jid ,
Nick ) ,
From , Err ) ,
StateData ;
{ true , _ , _ } - >
2007-06-25 18:43:42 +02:00
Lang = xml : get_attr_s ( " xml:lang " , Attrs ) ,
2009-04-08 21:04:13 +02:00
ErrText = " That nickname is already in use by another occupant " ,
2007-06-25 18:43:42 +02:00
Err = jlib : make_error_reply (
Packet ,
? ERRT_CONFLICT ( Lang , ErrText ) ) ,
ejabberd_router : route (
jlib : jid_replace_resource (
StateData #state.jid ,
Nick ) , % TODO: s/Nick/""/
From , Err ) ,
StateData ;
2008-07-23 14:31:55 +02:00
{ _ , false , _ } - >
2009-04-08 21:04:13 +02:00
ErrText = " That nickname is registered by another person " ,
2007-06-25 18:43:42 +02:00
Err = jlib : make_error_reply (
Packet ,
? ERRT_CONFLICT ( Lang , ErrText ) ) ,
ejabberd_router : route (
% TODO: s/Nick/""/
jlib : jid_replace_resource (
StateData #state.jid ,
Nick ) ,
From , Err ) ,
StateData ;
_ - >
change_nick ( From , Nick , StateData )
end ;
2008-07-23 14:31:55 +02:00
_ NotNickChange - >
2008-07-23 18:55:46 +02:00
Stanza = case { ( StateData #state.config ) #config.allow_visitor_status ,
2008-07-23 18:40:17 +02:00
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 " ,
[ jlib : jid_to_string ( StateData #state.jid ) ] ) ,
2009-12-29 15:43:24 +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 ) - >
LJID = jlib : jid_tolower ( JID ) ,
? DICT : is_key ( LJID , StateData #state.users ) .
2010-03-29 20:51:04 +02:00
%% Check if the user is occupant of the room, or at least is an admin or owner.
is_occupant_or_admin ( JID , StateData ) - >
2010-03-29 20:51:24 +02:00
FAffiliation = get_affiliation ( JID , StateData ) ,
FRole = get_role ( JID , StateData ) ,
case ( FRole / = none ) orelse
( FAffiliation == admin ) orelse
( FAffiliation == owner ) of
2010-03-29 20:51:04 +02:00
true - >
2010-03-29 20:51:24 +02:00
true ;
2010-03-29 20:51:04 +02:00
_ - >
2010-03-29 20:51:24 +02:00
false
end .
2009-12-01 21:00:15 +01:00
%%%
%%% Handle IQ queries of vCard
%%%
is_user_online_iq ( StanzaId , JID , StateData ) when JID #jid.lresource / = " " - >
{ is_user_online ( JID , StateData ) , StanzaId , JID } ;
is_user_online_iq ( StanzaId , JID , StateData ) when JID #jid.lresource == " " - >
try stanzaid_unpack ( StanzaId ) of
{ OriginalId , Resource } - >
JIDWithResource = jlib : 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 = jlib : jid_remove_resource ( ToJID ) ,
IQ = jlib : iq_query_info ( Packet ) ,
handle_iq_vcard2 ( FromFull , ToJID , ToBareJID , StanzaId , NewId , IQ , Packet ) .
handle_iq_vcard2 ( _ FromFull , ToJID , ToBareJID , StanzaId , _ NewId ,
#iq { type = get , xmlns = ? NS_VCARD } , Packet )
when ToBareJID / = ToJID - >
{ ToBareJID , change_stanzaid ( StanzaId , ToJID , Packet ) } ;
handle_iq_vcard2 ( _ FromFull , ToJID , _ ToBareJID , _ 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 ) - >
{ xmlelement , Name , Attrs , Els } = jlib : remove_attr ( " id " , Packet ) ,
{ xmlelement , Name , [ { " id " , NewId } | Attrs ] , Els } .
change_stanzaid ( PreviousId , ToJID , Packet ) - >
NewId = stanzaid_pack ( PreviousId , ToJID #jid.lresource ) ,
change_stanzaid ( NewId , Packet ) .
%%%
%%%
2003-03-23 21:08:44 +01:00
role_to_list ( Role ) - >
case Role of
moderator - > " moderator " ;
participant - > " participant " ;
visitor - > " visitor " ;
none - > " none "
end .
affiliation_to_list ( Affiliation ) - >
case Affiliation of
owner - > " owner " ;
admin - > " admin " ;
member - > " member " ;
outcast - > " outcast " ;
none - > " none "
end .
list_to_role ( Role ) - >
case Role of
" moderator " - > moderator ;
" participant " - > participant ;
" visitor " - > visitor ;
" none " - > none
end .
list_to_affiliation ( Affiliation ) - >
case Affiliation of
" owner " - > owner ;
" admin " - > admin ;
" member " - > member ;
" outcast " - > outcast ;
" none " - > none
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}
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 " ,
[ jlib : jid_to_string ( From ) ] ) ,
{ 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-01-29 15:49:08 +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 ;
_ - > false
end .
2003-03-23 21:08:44 +01:00
2008-02-09 11:38:47 +01:00
get_error_condition ( Packet ) - >
case catch get_error_condition2 ( Packet ) of
{ condition , ErrorCondition } - >
ErrorCondition ;
2008-02-11 13:15:34 +01:00
{ 'EXIT' , _ } - >
2008-02-09 11:38:47 +01:00
" badformed error stanza "
end .
get_error_condition2 ( Packet ) - >
{ xmlelement , _ , _ , EEls } = xml : get_subtag ( Packet , " error " ) ,
[ Condition ] = [ Name | | { xmlelement , Name , [ { " xmlns " , ? NS_STANZAS } ] , [ ] } < - EEls ] ,
{ condition , Condition } .
expulse_participant ( Packet , From , StateData , Reason1 ) - >
ErrorCondition = get_error_condition ( Packet ) ,
Reason2 = io_lib : format ( Reason1 ++ " : " ++ " ~s " , [ ErrorCondition ] ) ,
NewState = add_user_presence_un (
From ,
{ xmlelement , " presence " ,
[ { " type " , " unavailable " } ] ,
[ { xmlelement , " status " , [ ] ,
[ { xmlcdata , Reason2 } ]
} ] } ,
StateData ) ,
send_new_presence ( From , NewState ) ,
remove_online_user ( From , NewState ) .
2003-03-23 21:08:44 +01:00
set_affiliation ( JID , Affiliation , StateData ) - >
2011-09-05 12:53:01 +02:00
set_affiliation ( JID , Affiliation , StateData , " " ) .
2003-03-23 21:08:44 +01:00
2011-09-05 12:53:01 +02:00
set_affiliation ( JID , Affiliation , StateData , Reason ) - >
2005-05-04 01:07:14 +02:00
LJID = jlib : jid_remove_resource ( jlib : jid_tolower ( JID ) ) ,
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 ;
_ - >
LJID = jlib : jid_tolower ( JID ) ,
case ? DICT : find ( LJID , StateData #state.affiliations ) of
{ ok , Affiliation } - >
Affiliation ;
_ - >
LJID1 = jlib : jid_remove_resource ( LJID ) ,
case ? DICT : find ( LJID1 , StateData #state.affiliations ) of
{ ok , Affiliation } - >
Affiliation ;
_ - >
LJID2 = setelement ( 1 , LJID , " " ) ,
case ? DICT : find ( LJID2 , StateData #state.affiliations ) of
{ ok , Affiliation } - >
Affiliation ;
_ - >
LJID3 = jlib : jid_remove_resource ( LJID2 ) ,
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 ) - >
LJID = jlib : jid_tolower ( JID ) ,
2003-04-13 21:22:46 +02:00
LJIDs = case LJID of
{ U , S , " " } - >
? 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 ,
2009-08-21 15:22:18 +02:00
{ Users , Nicks }
= case Role of
none - >
lists : foldl ( fun ( J , { Us , Ns } ) - >
NewNs =
case ? DICT : find ( J , Us ) of
{ ok , #user { nick = Nick } } - >
? DICT : erase ( Nick , Ns ) ;
_ - >
Ns
end ,
{ ? DICT : erase ( J , Us ) , NewNs }
end ,
{ StateData #state.users , StateData #state.nicks } ,
LJIDs ) ;
_ - >
{ lists : foldl ( fun ( J , Us ) - >
{ ok , User } = ? DICT : find ( J , Us ) ,
? DICT : store ( J ,
User #user { role = Role } ,
Us )
end , StateData #state.users , LJIDs ) ,
StateData #state.nicks }
end ,
StateData #state { users = Users , nicks = Nicks } .
2003-03-23 21:08:44 +01:00
get_role ( JID , StateData ) - >
LJID = jlib : jid_tolower ( JID ) ,
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-07-23 14:31:55 +02:00
is_visitor ( Jid , StateData ) - >
2008-08-24 01:17:17 +02:00
get_role ( Jid , StateData ) =:= visitor .
2008-07-23 14:31:55 +02:00
2011-07-11 17:33:26 +02:00
is_moderator ( Jid , StateData ) - >
get_role ( Jid , StateData ) =:= moderator .
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-11-28 17:06:39 +01:00
case treap : lookup ( jlib : jid_tolower ( 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-11-28 17:06:39 +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 : jid_tolower ( 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 ,
2010-07-01 12:54:01 +02:00
Size = element_size ( 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 ,
2010-07-01 12:54:01 +02:00
Size = element_size ( 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 ) - >
LJID = jlib : jid_tolower ( JID ) ,
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 ) ,
2009-08-21 15:22:18 +02:00
Nicks = ? DICT : update ( Nick ,
fun ( Entry ) - >
case lists : member ( LJID , Entry ) of
true - >
Entry ;
false - >
[ LJID | Entry ]
end
end ,
[ LJID ] ,
StateData #state.nicks ) ,
2007-12-03 11:47:42 +01:00
tab_add_online_user ( JID , StateData ) ,
2009-08-21 15:22:18 +02:00
StateData #state { users = Users , nicks = Nicks } .
2003-03-23 21:08:44 +01:00
remove_online_user ( JID , StateData ) - >
2006-03-14 05:26:15 +01:00
remove_online_user ( JID , StateData , " " ) .
remove_online_user ( JID , StateData , Reason ) - >
2003-03-23 21:08:44 +01:00
LJID = jlib : jid_tolower ( 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 ) ,
2009-08-21 15:22:18 +02:00
Nicks = case ? DICT : find ( Nick , StateData #state.nicks ) of
{ ok , [ LJID ] } - >
? DICT : erase ( Nick , StateData #state.nicks ) ;
{ ok , U } - >
? DICT : store ( Nick , U -- [ LJID ] , StateData #state.nicks ) ;
2011-09-05 09:27:15 +02:00
error - >
2009-08-21 15:22:18 +02:00
StateData #state.nicks
end ,
StateData #state { users = Users , nicks = Nicks } .
2003-03-23 21:08:44 +01:00
filter_presence ( { xmlelement , " presence " , Attrs , Els } ) - >
FEls = lists : filter (
fun ( El ) - >
case El of
{ xmlcdata , _ } - >
false ;
2006-01-19 03:17:31 +01:00
{ xmlelement , _ Name1 , Attrs1 , _ Els1 } - >
2005-04-17 23:39:41 +02:00
XMLNS = xml : get_attr_s ( " xmlns " , Attrs1 ) ,
2006-01-19 03:17:31 +01:00
case XMLNS of
? NS_MUC ++ _ - >
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
end , Els ) ,
{ xmlelement , " presence " , Attrs , FEls } .
2008-07-23 18:40:17 +02:00
strip_status ( { xmlelement , " presence " , Attrs , Els } ) - >
FEls = lists : filter (
fun ( { xmlelement , " status " , _ Attrs1 , _ Els1 } ) - >
false ;
( _ ) - > true
end , Els ) ,
{ xmlelement , " presence " , Attrs , FEls } .
2003-03-23 21:08:44 +01:00
add_user_presence ( JID , Presence , StateData ) - >
LJID = jlib : jid_tolower ( JID ) ,
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 ) - >
LJID = jlib : jid_tolower ( JID ) ,
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 } .
2011-08-23 21:53:30 +02:00
%% Find and return a list of the full JIDs of the users of Nick.
%% Return jid record.
find_jids_by_nick ( Nick , StateData ) - >
case ? DICT : find ( Nick , StateData #state.nicks ) of
{ ok , [ User ] } - >
[ jlib : make_jid ( User ) ] ;
{ ok , Users } - >
[ jlib : make_jid ( LJID ) | | LJID < - Users ] ;
error - >
false
end .
2009-08-21 15:22:18 +02:00
%% Find and return the full JID of the user of Nick with
%% highest-priority presence. Return jid record.
2003-03-23 21:08:44 +01:00
find_jid_by_nick ( Nick , StateData ) - >
2009-08-21 15:22:18 +02:00
case ? DICT : find ( Nick , StateData #state.nicks ) of
{ ok , [ User ] } - >
jlib : make_jid ( User ) ;
{ ok , [ FirstUser | Users ] } - >
#user { last_presence = FirstPresence } =
? DICT : fetch ( FirstUser , StateData #state.users ) ,
{ LJID , _ } =
lists : foldl ( fun ( Compare , { HighestUser , HighestPresence } ) - >
#user { last_presence = P1 } =
? DICT : fetch ( Compare , StateData #state.users ) ,
case higher_presence ( P1 , HighestPresence ) of
true - >
{ Compare , P1 } ;
false - >
{ HighestUser , HighestPresence }
end
end , { FirstUser , FirstPresence } , Users ) ,
jlib : make_jid ( LJID ) ;
error - >
false
end .
higher_presence ( Pres1 , Pres2 ) - >
Pri1 = get_priority_from_presence ( Pres1 ) ,
Pri2 = get_priority_from_presence ( Pres2 ) ,
Pri1 > Pri2 .
get_priority_from_presence ( PresencePacket ) - >
case xml : get_subtag ( PresencePacket , " priority " ) of
false - >
0 ;
SubEl - >
case catch list_to_integer ( xml : get_tag_cdata ( SubEl ) ) of
P when is_integer ( P ) - >
P ;
_ - >
0
end
end .
2003-03-23 21:08:44 +01:00
is_nick_change ( JID , Nick , StateData ) - >
LJID = jlib : jid_tolower ( JID ) ,
case Nick of
" " - >
false ;
_ - >
{ ok , #user { nick = OldNick } } =
? DICT : find ( LJID , StateData #state.users ) ,
Nick / = OldNick
end .
2009-08-21 15:22:18 +02:00
nick_collision ( User , Nick , StateData ) - >
UserOfNick = find_jid_by_nick ( Nick , StateData ) ,
%% if nick is not used, or is used by another resource of the same
%% user, it's ok.
UserOfNick / = false andalso
jlib : jid_remove_resource ( jlib : jid_tolower ( UserOfNick ) ) / =
jlib : jid_remove_resource ( jlib : jid_tolower ( User ) ) .
2003-08-15 21:17:12 +02:00
add_new_user ( From , Nick , { xmlelement , _ , Attrs , Els } = Packet , StateData ) - >
2004-03-02 22:16:55 +01:00
Lang = xml : get_attr_s ( " xml:lang " , Attrs ) ,
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 ) ,
2009-08-21 15:22:18 +02:00
Collision = nick_collision ( From , Nick , StateData ) ,
2007-12-03 11:47:42 +01:00
case { ( ServiceAffiliation == owner orelse
( ( Affiliation == admin orelse Affiliation == owner ) andalso
NUsers < MaxAdminUsers ) orelse
NUsers < MaxUsers ) andalso
NConferences < MaxConferences ,
2009-08-21 15:22:18 +02:00
Collision ,
2007-05-16 12:13:04 +02:00
mod_muc : can_use_nick ( StateData #state.host , From , Nick ) ,
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
Err = jlib : make_error_reply (
Packet ,
? ERR_SERVICE_UNAVAILABLE ) ,
ejabberd_router : route ( % TODO: s/Nick/""/
jlib : jid_replace_resource ( StateData #state.jid , Nick ) ,
From , Err ) ,
StateData ;
{ _ , _ , _ , none } - >
2007-05-16 12:13:04 +02:00
Err = jlib : make_error_reply (
Packet ,
case Affiliation of
outcast - >
ErrText = " You have been banned from this room " ,
? ERRT_FORBIDDEN ( Lang , ErrText ) ;
_ - >
2009-04-08 21:04:13 +02:00
ErrText = " Membership is required to enter this room " ,
2007-05-16 12:13:04 +02:00
? ERRT_REGISTRATION_REQUIRED ( Lang , ErrText )
end ) ,
ejabberd_router : route ( % TODO: s/Nick/""/
jlib : jid_replace_resource ( StateData #state.jid , Nick ) ,
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 " ,
2004-03-02 22:16:55 +01:00
Err = jlib : make_error_reply ( Packet , ? ERRT_CONFLICT ( Lang , ErrText ) ) ,
ejabberd_router : route (
% TODO: s/Nick/""/
jlib : jid_replace_resource ( StateData #state.jid , Nick ) ,
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 " ,
2004-03-02 22:16:55 +01:00
Err = jlib : make_error_reply ( Packet , ? ERRT_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/""/
jlib : 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-03-13 17:01:46 +01:00
case check_password ( ServiceAffiliation , Affiliation ,
Els , From , 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 - >
WPacket = { xmlelement , " message " , [ { " type " , " groupchat " } ] ,
[ { xmlelement , " body " , [ ] ,
[ { xmlcdata , translate : translate (
Lang ,
" This room is not anonymous " ) } ] } ,
{ xmlelement , " x " , [ { " xmlns " , ? NS_MUC_USER } ] ,
[ { xmlelement , " status " , [ { " code " , " 100 " } ] , [ ] } ] } ] } ,
ejabberd_router : route (
StateData #state.jid ,
From , WPacket ) ;
true - >
ok
end ,
send_existing_presences ( From , NewState ) ,
send_new_presence ( From , NewState ) ,
Shift = count_stanza_shift ( Nick , Els , NewState ) ,
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-03-13 17:01:46 +01:00
Robots = ? DICT : erase ( From , StateData #state.robots ) ,
NewState #state { robots = Robots }
2007-05-16 12:13:04 +02:00
end ;
nopass - >
2009-04-08 21:04:13 +02:00
ErrText = " A password is required to enter this room " ,
2003-08-15 21:17:12 +02:00
Err = jlib : make_error_reply (
2007-05-16 12:13:04 +02:00
Packet , ? ERRT_NOT_AUTHORIZED ( Lang , ErrText ) ) ,
2003-08-15 21:17:12 +02:00
ejabberd_router : route ( % TODO: s/Nick/""/
2007-05-16 12:13:04 +02:00
jlib : jid_replace_resource (
StateData #state.jid , Nick ) ,
2003-08-15 21:17:12 +02:00
From , Err ) ,
StateData ;
2009-03-13 17:01:46 +01:00
captcha_required - >
SID = xml : get_attr_s ( " id " , Attrs ) ,
RoomJID = StateData #state.jid ,
To = jlib : jid_replace_resource ( RoomJID , Nick ) ,
2011-04-14 10:03:02 +02:00
Limiter = { From #jid.luser , From #jid.lserver } ,
2009-03-13 17:01:46 +01:00
case ejabberd_captcha : create_captcha (
2011-04-14 10:03:02 +02:00
SID , RoomJID , To , Lang , Limiter , From ) of
{ ok , ID , CaptchaEls } - >
2009-03-13 17:01:46 +01:00
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 } ;
2011-04-14 10:03:02 +02:00
{ error , limit } - >
ErrText = " Too many CAPTCHA requests " ,
Err = jlib : make_error_reply (
Packet , ? ERRT_RESOURCE_CONSTRAINT ( Lang , ErrText ) ) ,
ejabberd_router : route ( % TODO: s/Nick/""/
jlib : jid_replace_resource (
StateData #state.jid , Nick ) ,
From , Err ) ,
StateData ;
_ - >
2009-03-13 17:01:46 +01:00
ErrText = " Unable to generate a captcha " ,
Err = jlib : make_error_reply (
Packet , ? ERRT_INTERNAL_SERVER_ERROR ( Lang , ErrText ) ) ,
ejabberd_router : route ( % TODO: s/Nick/""/
jlib : 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 " ,
Err = jlib : make_error_reply (
Packet , ? ERRT_NOT_AUTHORIZED ( Lang , ErrText ) ) ,
ejabberd_router : route ( % TODO: s/Nick/""/
jlib : jid_replace_resource (
StateData #state.jid , Nick ) ,
From , Err ) ,
StateData
end
2003-08-15 21:17:12 +02:00
end .
2009-03-13 17:01:46 +01:00
check_password ( owner , _ Affiliation , _ Els , _ From , _ StateData ) - >
2009-02-16 16:57:02 +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-03-13 17:01:46 +01: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-03-13 17:01:46 +01: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-03-13 17:01:46 +01:00
false
2004-03-02 22:16:55 +01:00
end
2003-08-15 21:17:12 +02:00
end
end .
2009-03-13 17:01:46 +01:00
check_captcha ( Affiliation , From , StateData ) - >
2009-05-26 13:53:58 +02:00
case ( StateData #state.config ) #config.captcha_protected
andalso ejabberd_captcha : is_feature_available ( ) of
2009-03-13 17:01:46 +01:00
true when Affiliation == none - >
case ? DICT : find ( From , StateData #state.robots ) of
{ ok , passed } - >
true ;
_ - >
2011-02-16 11:14:39 +01:00
WList = ( StateData #state.config ) #config.captcha_whitelist ,
#jid { luser = U , lserver = S , lresource = R } = From ,
case ? SETS : is_element ( { U , S , R } , WList ) of
true - >
true ;
false - >
case ? SETS : is_element ( { U , S , " " } , WList ) of
true - >
true ;
false - >
case ? SETS : is_element ( { " " , S , " " } , WList ) of
true - >
true ;
false - >
captcha_required
end
end
end
2009-03-13 17:01:46 +01:00
end ;
_ - >
true
end .
2003-08-15 21:17:12 +02:00
extract_password ( [ ] ) - >
2004-03-02 22:16:55 +01:00
false ;
extract_password ( [ { xmlelement , _ Name , Attrs , _ SubEls } = El | Els ] ) - >
2003-08-15 21:17:12 +02:00
case xml : get_attr_s ( " xmlns " , Attrs ) of
? NS_MUC - >
2004-03-02 22:16:55 +01:00
case xml : get_subtag ( El , " password " ) of
false - >
false ;
SubEl - >
xml : get_tag_cdata ( SubEl )
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 ;
2004-05-17 22:36:41 +02:00
extract_history ( [ { xmlelement , _ Name , Attrs , _ SubEls } = El | Els ] , Type ) - >
2004-02-15 21:10:40 +01:00
case xml : get_attr_s ( " xmlns " , Attrs ) of
? NS_MUC - >
AttrVal = xml : get_path_s ( El ,
[ { elem , " history " } , { attr , Type } ] ) ,
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 ) - >
2008-07-30 20:24:08 +02:00
send_update_presence ( JID , " " , StateData ) .
send_update_presence ( JID , Reason , StateData ) - >
2003-04-13 21:22:46 +02:00
LJID = jlib : jid_tolower ( JID ) ,
LJIDs = case LJID of
{ U , S , " " } - >
? 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-07-30 20:24:08 +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 ) - >
2008-07-30 20:24:08 +02:00
send_new_presence ( NJID , " " , StateData ) .
send_new_presence ( NJID , Reason , StateData ) - >
2009-08-21 15:22:18 +02:00
%% First, find the nick associated with this JID.
#user { nick = Nick } = ? DICT : fetch ( jlib : jid_tolower ( NJID ) , StateData #state.users ) ,
%% Then find the JID using this nick with highest priority.
LJID = find_jid_by_nick ( Nick , StateData ) ,
%% Then we get the presence data we're supposed to send.
2003-03-23 21:08:44 +01:00
{ ok , #user { jid = RealJID ,
role = Role ,
last_presence = Presence } } =
2009-08-21 15:22:18 +02:00
? DICT : find ( jlib : jid_tolower ( LJID ) , StateData #state.users ) ,
Affiliation = get_affiliation ( LJID , StateData ) ,
2003-03-23 21:08:44 +01:00
SAffiliation = affiliation_to_list ( Affiliation ) ,
SRole = role_to_list ( Role ) ,
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 - >
[ { " jid " , jlib : jid_to_string ( RealJID ) } ,
{ " affiliation " , SAffiliation } ,
{ " role " , SRole } ] ;
_ - >
[ { " affiliation " , SAffiliation } ,
{ " role " , SRole } ]
end ,
2008-07-30 20:24:08 +02:00
ItemEls = case Reason of
" " - >
[ ] ;
_ - >
[ { xmlelement , " reason " , [ ] ,
[ { xmlcdata , Reason } ] } ]
end ,
2004-05-01 22:10:25 +02:00
Status = case StateData #state.just_created of
true - >
[ { xmlelement , " status " , [ { " code " , " 201 " } ] , [ ] } ] ;
false - >
[ ]
end ,
2010-08-07 20:48:36 +02:00
Status2 = case ( ( StateData #state.config ) #config.anonymous == false )
andalso ( NJID == Info #user.jid ) of
true - >
[ { xmlelement , " status " , [ { " code " , " 100 " } ] , [ ] }
| Status ] ;
false - >
Status
end ,
2011-07-16 19:26:20 +02:00
Status3 = case NJID == Info #user.jid of
true - >
[ { xmlelement , " status " , [ { " code " , " 110 " } ] , [ ] }
| Status2 ] ;
false - >
Status2
end ,
2009-06-30 19:50:47 +02:00
Packet = xml : append_subtags (
2003-03-23 21:08:44 +01:00
Presence ,
[ { xmlelement , " x " , [ { " xmlns " , ? NS_MUC_USER } ] ,
2011-07-16 19:26:20 +02:00
[ { xmlelement , " item " , ItemAttrs , ItemEls } | Status3 ] } ] ) ,
2003-03-23 21:08:44 +01:00
ejabberd_router : route (
2003-10-07 22:31:44 +02:00
jlib : 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 ) - >
LToJID = jlib : jid_tolower ( ToJID ) ,
{ ok , #user { jid = RealToJID ,
role = Role } } =
? DICT : find ( LToJID , StateData #state.users ) ,
lists : foreach (
2009-08-21 15:22:18 +02:00
fun ( { FromNick , _ Users } ) - >
LJID = find_jid_by_nick ( FromNick , StateData ) ,
#user { jid = FromJID ,
role = FromRole ,
last_presence = Presence
} = ? DICT : fetch ( jlib : jid_tolower ( LJID ) , StateData #state.users ) ,
2003-03-23 21:08:44 +01:00
case RealToJID of
FromJID - >
ok ;
_ - >
FromAffiliation = get_affiliation ( LJID , StateData ) ,
2003-03-26 21:51:18 +01:00
ItemAttrs =
case ( Role == moderator ) orelse
( ( StateData #state.config ) #config.anonymous ==
false ) of
true - >
[ { " jid " , jlib : jid_to_string ( FromJID ) } ,
{ " affiliation " ,
affiliation_to_list ( FromAffiliation ) } ,
{ " role " , role_to_list ( FromRole ) } ] ;
_ - >
[ { " affiliation " ,
affiliation_to_list ( FromAffiliation ) } ,
{ " role " , role_to_list ( FromRole ) } ]
end ,
2009-06-30 19:50:47 +02:00
Packet = xml : append_subtags (
2003-03-23 21:08:44 +01:00
Presence ,
[ { xmlelement , " x " , [ { " xmlns " , ? NS_MUC_USER } ] ,
[ { xmlelement , " item " , ItemAttrs , [ ] } ] } ] ) ,
ejabberd_router : route (
2003-10-07 22:31:44 +02:00
jlib : jid_replace_resource (
StateData #state.jid , FromNick ) ,
2003-03-23 21:08:44 +01:00
RealToJID ,
Packet )
end
2009-08-21 15:22:18 +02:00
end , ? DICT : to_list ( StateData #state.nicks ) ) .
2003-03-23 21:08:44 +01:00
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 ) - >
LJID = jlib : jid_tolower ( JID ) ,
{ 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 ) ,
2009-08-21 15:22:18 +02:00
OldNickUsers = ? DICT : fetch ( OldNick , StateData #state.nicks ) ,
NewNickUsers = case ? DICT : find ( Nick , StateData #state.nicks ) of
{ ok , U } - > U ;
error - > [ ]
end ,
%% Send unavailable presence from the old nick if it's no longer
%% used.
SendOldUnavailable = length ( OldNickUsers ) == 1 ,
%% If we send unavailable presence from the old nick, we should
%% probably send presence from the new nick, in order not to
%% confuse clients. Otherwise, do it only if the new nick was
%% unused.
SendNewAvailable = SendOldUnavailable orelse
NewNickUsers == [ ] ,
Nicks =
case OldNickUsers of
[ LJID ] - >
? DICT : store ( Nick , [ LJID | NewNickUsers ] ,
? DICT : erase ( OldNick , StateData #state.nicks ) ) ;
[ _ | _ ] - >
? DICT : store ( Nick , [ LJID | NewNickUsers ] ,
? DICT : store ( OldNick , OldNickUsers -- [ LJID ] ,
StateData #state.nicks ) )
end ,
NewStateData = StateData #state { users = Users , nicks = Nicks } ,
send_nick_changing ( JID , OldNick , NewStateData , SendOldUnavailable , SendNewAvailable ) ,
2006-03-14 05:26:15 +01:00
add_to_log ( nickchange , { OldNick , Nick } , StateData ) ,
2003-03-23 21:08:44 +01:00
NewStateData .
2009-08-21 15:22:18 +02:00
send_nick_changing ( JID , OldNick , StateData ,
SendOldUnavailable , SendNewAvailable ) - >
2003-03-23 21:08:44 +01:00
{ ok , #user { jid = RealJID ,
nick = Nick ,
role = Role ,
last_presence = Presence } } =
? DICT : find ( jlib : jid_tolower ( JID ) , StateData #state.users ) ,
Affiliation = get_affiliation ( JID , StateData ) ,
SAffiliation = affiliation_to_list ( Affiliation ) ,
SRole = role_to_list ( Role ) ,
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 - >
[ { " jid " , jlib : jid_to_string ( RealJID ) } ,
{ " affiliation " , SAffiliation } ,
{ " role " , SRole } ,
{ " nick " , Nick } ] ;
_ - >
[ { " affiliation " , SAffiliation } ,
{ " role " , SRole } ,
{ " nick " , Nick } ]
end ,
ItemAttrs2 =
case ( Info #user.role == moderator ) orelse
( ( StateData #state.config ) #config.anonymous == false ) of
true - >
[ { " jid " , jlib : jid_to_string ( RealJID ) } ,
{ " affiliation " , SAffiliation } ,
{ " role " , SRole } ] ;
_ - >
[ { " affiliation " , SAffiliation } ,
{ " role " , SRole } ]
end ,
2003-03-23 21:08:44 +01:00
Packet1 =
{ xmlelement , " presence " , [ { " type " , " unavailable " } ] ,
[ { xmlelement , " x " , [ { " xmlns " , ? NS_MUC_USER } ] ,
[ { xmlelement , " item " , ItemAttrs1 , [ ] } ,
{ xmlelement , " status " , [ { " code " , " 303 " } ] , [ ] } ] } ] } ,
2009-06-30 19:50:47 +02:00
Packet2 = xml : append_subtags (
2003-03-23 21:08:44 +01:00
Presence ,
[ { xmlelement , " x " , [ { " xmlns " , ? NS_MUC_USER } ] ,
[ { xmlelement , " item " , ItemAttrs2 , [ ] } ] } ] ) ,
2009-08-21 15:22:18 +02:00
if SendOldUnavailable - >
ejabberd_router : route (
jlib : jid_replace_resource ( StateData #state.jid , OldNick ) ,
Info #user.jid ,
Packet1 ) ;
true - >
ok
end ,
if SendNewAvailable - >
ejabberd_router : route (
jlib : jid_replace_resource ( StateData #state.jid , Nick ) ,
Info #user.jid ,
Packet2 ) ;
true - >
ok
end
2003-03-23 21:08:44 +01:00
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:51:25 +02:00
add_message_to_history ( FromNick , FromJID , Packet , StateData ) - >
2003-03-23 21:08:44 +01:00
HaveSubject = case xml : get_subtag ( Packet , " subject " ) of
false - >
false ;
_ - >
true
end ,
2004-02-15 21:10:40 +01:00
TimeStamp = calendar : now_to_universal_time ( now ( ) ) ,
2009-06-30 18:51:25 +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 ,
2009-06-30 19:50:47 +02:00
TSPacket = xml : append_subtags ( Packet ,
2009-06-30 18:51:25 +02:00
[ jlib : timestamp_to_xml ( TimeStamp , utc , SenderJid , " " ) ,
%% TODO: Delete the next line once XEP-0091 is Obsolete
jlib : timestamp_to_xml ( TimeStamp ) ] ) ,
2004-02-15 21:10:40 +01:00
SPacket = jlib : replace_from_to (
jlib : jid_replace_resource ( StateData #state.jid , FromNick ) ,
StateData #state.jid ,
TSPacket ) ,
2010-07-01 12:54:01 +02:00
Size = element_size ( 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 (
2003-10-07 22:31:44 +02:00
jlib : 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
" " - >
ok ;
Nick - >
Subject = StateData #state.subject ,
Packet = { xmlelement , " message " , [ { " type " , " groupchat " } ] ,
[ { xmlelement , " subject " , [ ] , [ { xmlcdata , Subject } ] } ,
{ xmlelement , " body " , [ ] ,
[ { xmlcdata ,
2004-02-26 23:00:04 +01:00
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 ) - >
case xml : get_subtag ( Packet , " subject " ) of
false - >
false ;
SubjEl - >
xml : get_tag_cdata ( SubjEl )
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 ) - >
2003-03-23 21:08:44 +01:00
{ xmlelement , _ , _ , 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 ) - >
2003-03-23 21:08:44 +01:00
case xml : get_subtag ( SubEl , " item " ) of
false - >
{ error , ? ERR_BAD_REQUEST } ;
Item - >
FAffiliation = get_affiliation ( From , StateData ) ,
FRole = get_role ( From , StateData ) ,
case xml : get_tag_attr ( " role " , Item ) of
false - >
case xml : get_tag_attr ( " affiliation " , Item ) of
false - >
{ error , ? ERR_BAD_REQUEST } ;
{ value , StrAffiliation } - >
case catch list_to_affiliation ( StrAffiliation ) of
{ 'EXIT' , _ } - >
{ error , ? ERR_BAD_REQUEST } ;
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 " ,
{ error , ? ERRT_FORBIDDEN ( Lang , ErrText ) }
2003-03-23 21:08:44 +01:00
end
end
end ;
{ value , StrRole } - >
case catch list_to_role ( StrRole ) of
{ 'EXIT' , _ } - >
{ error , ? ERR_BAD_REQUEST } ;
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 " ,
{ error , ? ERRT_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 } } ) - >
{ xmlelement , " item " ,
[ { " affiliation " , affiliation_to_list ( Affiliation ) } ,
{ " jid " , jlib : jid_to_string ( JID ) } ] ,
[ { xmlelement , " reason " , [ ] , [ { xmlcdata , Reason } ] } ] } ;
( { JID , Affiliation } ) - >
2003-03-23 21:08:44 +01:00
{ xmlelement , " item " ,
[ { " affiliation " , affiliation_to_list ( Affiliation ) } ,
{ " jid " , jlib : jid_to_string ( JID ) } ] ,
[ ] }
end , search_affiliation ( SAffiliation , StateData ) ) .
user_to_item ( #user { role = Role ,
nick = Nick ,
jid = JID
} , StateData ) - >
Affiliation = get_affiliation ( JID , StateData ) ,
{ xmlelement , " item " ,
[ { " role " , role_to_list ( Role ) } ,
{ " affiliation " , affiliation_to_list ( Affiliation ) } ,
{ " nick " , Nick } ,
{ " jid " , jlib : jid_to_string ( JID ) } ] ,
[ ] } .
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 " ,
[ jlib : jid_to_string ( UJID ) , jlib : jid_to_string ( StateData #state.jid ) , Res ] ) ,
2003-03-23 21:08:44 +01:00
NSD =
lists : foldl (
fun ( E , SD ) - >
case catch (
case E of
2007-12-23 13:28:44 +01:00
{ JID , affiliation , owner , _ }
when ( JID #jid.luser == " " ) - >
%% If the provided JID does not have username,
%% forget the affiliation completely
SD ;
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 (
2010-11-16 00:03:09 +01:00
JID , Reason , " 321 " , none , SD ) ,
2006-02-06 06:12:54 +01:00
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 (
2010-11-16 00:03:09 +01:00
JID , Reason , " 301 " , outcast , SD ) ,
2011-09-05 12:53:01 +02:00
set_affiliation (
JID , outcast ,
set_role ( JID , none , SD ) , Reason ) ;
2008-07-30 20:24:08 +02:00
{ JID , affiliation , A , Reason } when
2004-12-05 21:54:55 +01:00
( A == admin ) or ( A == owner ) - >
2011-09-05 12:53:01 +02:00
SD1 = set_affiliation ( JID , A , SD , Reason ) ,
2003-03-28 21:20:49 +01:00
SD2 = set_role ( JID , moderator , SD1 ) ,
2008-07-30 20:24:08 +02:00
send_update_presence ( JID , Reason , SD2 ) ,
2003-03-28 21:20:49 +01:00
SD2 ;
2008-07-30 20:24:08 +02:00
{ JID , affiliation , member , Reason } - >
2011-09-05 12:53:01 +02:00
SD1 = set_affiliation (
JID , member , SD , Reason ) ,
2003-03-28 21:20:49 +01:00
SD2 = set_role ( JID , participant , SD1 ) ,
2008-07-30 20:24:08 +02:00
send_update_presence ( JID , Reason , SD2 ) ,
2003-03-28 21:20:49 +01:00
SD2 ;
2008-07-30 20:24:08 +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
) 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
2011-09-08 16:10:38 +02:00
end , StateData , lists : flatten ( 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 } ;
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 ,
2004-05-17 22:36:41 +02:00
[ { xmlelement , " item " , Attrs , _ Els } = Item | Items ] ,
2004-03-02 22:16:55 +01:00
Lang , StateData , Res ) - >
2003-03-23 21:08:44 +01:00
TJID = case xml : get_attr ( " jid " , Attrs ) of
{ value , S } - >
case jlib : string_to_jid ( S ) of
error - >
2004-03-02 22:16:55 +01:00
ErrText = io_lib : format (
translate : translate (
Lang ,
2009-01-12 19:41:46 +01:00
" Jabber ID ~s is invalid " ) , [ S ] ) ,
2004-03-02 22:16:55 +01:00
{ error , ? ERRT_NOT_ACCEPTABLE ( Lang , ErrText ) } ;
2003-03-23 21:08:44 +01:00
J - >
2011-09-09 04:34:06 +02:00
{ value , [ J ] }
2003-03-23 21:08:44 +01:00
end ;
_ - >
case xml : get_attr ( " nick " , Attrs ) of
{ value , N } - >
2011-09-08 16:10:38 +02:00
case find_jids_by_nick ( N , StateData ) of
2003-03-23 21:08:44 +01:00
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 ] ) ,
{ error , ? ERRT_NOT_ACCEPTABLE ( Lang , ErrText ) } ;
2003-03-23 21:08:44 +01:00
J - >
2011-09-09 04:40:07 +02:00
{ value , J }
2003-03-23 21:08:44 +01:00
end ;
_ - >
{ error , ? ERR_BAD_REQUEST }
end
end ,
case TJID of
2011-09-08 16:10:38 +02:00
{ value , [ JID | _ ] = JIDs } - >
2003-03-23 21:08:44 +01:00
TAffiliation = get_affiliation ( JID , StateData ) ,
TRole = get_role ( JID , StateData ) ,
case xml : get_attr ( " role " , Attrs ) of
false - >
case xml : get_attr ( " affiliation " , Attrs ) of
false - >
{ error , ? ERR_BAD_REQUEST } ;
{ value , StrAffiliation } - >
case catch list_to_affiliation ( StrAffiliation ) of
{ 'EXIT' , _ } - >
2004-03-02 22:16:55 +01:00
ErrText1 =
io_lib : format (
translate : translate (
Lang ,
" Invalid affiliation: ~s " ) ,
[ StrAffiliation ] ) ,
{ error , ? ERRT_NOT_ACCEPTABLE ( Lang , ErrText1 ) } ;
2003-03-23 21:08:44 +01:00
SAffiliation - >
2008-07-17 19:32:11 +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-07-17 19:32:11 +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 , _ } ] - >
jlib : jid_remove_resource ( OJID ) / =
jlib : jid_tolower ( jlib : jid_remove_resource ( UJID ) ) ;
_ - >
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 - >
2011-09-08 16:10:38 +02:00
Reason = xml : get_path_s ( Item , [ { elem , " reason " } , cdata ] ) ,
MoreRes = [ { jlib : jid_remove_resource ( Jidx ) , affiliation , SAffiliation , Reason } | | Jidx < - JIDs ] ,
2003-03-23 21:08:44 +01:00
find_changed_items (
UJID ,
UAffiliation , URole ,
2004-03-02 22:16:55 +01:00
Items , Lang , StateData ,
2011-09-08 16:10:38 +02:00
[ MoreRes | Res ] ) ;
2006-03-06 03:30:15 +01:00
false - >
2003-03-23 21:08:44 +01:00
{ error , ? ERR_NOT_ALLOWED }
end
end
end ;
{ value , StrRole } - >
case catch list_to_role ( StrRole ) of
{ 'EXIT' , _ } - >
2004-03-02 22:16:55 +01:00
ErrText1 =
io_lib : format (
translate : translate (
Lang ,
" Invalid role: ~s " ) ,
[ StrRole ] ) ,
{ error , ? ERRT_BAD_REQUEST ( Lang , ErrText1 ) } ;
2003-03-23 21:08:44 +01:00
SRole - >
2008-07-17 19:32:11 +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-07-17 19:32:11 +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 , _ } ] - >
jlib : jid_remove_resource ( OJID ) / =
jlib : jid_tolower ( jlib : jid_remove_resource ( UJID ) ) ;
_ - >
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 - >
2011-09-08 16:10:38 +02:00
Reason = xml : get_path_s ( Item , [ { elem , " reason " } , cdata ] ) ,
MoreRes = [ { Jidx , role , SRole , Reason } | | Jidx < - JIDs ] ,
2003-03-23 21:08:44 +01:00
find_changed_items (
UJID ,
UAffiliation , URole ,
2004-03-02 22:16:55 +01:00
Items , Lang , StateData ,
2011-09-08 16:10:38 +02:00
[ MoreRes | Res ] ) ;
2003-03-23 21:08:44 +01:00
_ - >
{ error , ? ERR_NOT_ALLOWED }
end
end
end ;
Err - >
Err
end ;
2004-05-17 22:36:41 +02:00
find_changed_items ( _ UJID , _ UAffiliation , _ URole , _ Items ,
_ Lang , _ StateData , _ Res ) - >
2003-03-23 21:08:44 +01:00
{ error , ? ERR_BAD_REQUEST } .
2008-07-17 19:32:11 +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 ;
2011-04-17 17:03:07 +02:00
can_change_ra ( _ FAffiliation , _ FRole ,
_ TAffiliation , _ TRole ,
_ RoleorAffiliation , _ Value , owner ) - >
%% Nobody can decrease MUC admin's role/affiliation
false ;
2006-01-19 03:17:31 +01:00
can_change_ra ( _ FAffiliation , _ FRole ,
TAffiliation , _ TRole ,
2008-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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-07-17 19:32:11 +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 ) - >
2010-11-16 00:03:09 +01:00
NewAffiliation = get_affiliation ( JID , StateData ) ,
send_kickban_presence ( JID , Reason , Code , NewAffiliation , StateData ) .
send_kickban_presence ( JID , Reason , Code , NewAffiliation , StateData ) - >
2003-04-13 21:22:46 +02:00
LJID = jlib : jid_tolower ( JID ) ,
LJIDs = case LJID of
{ U , S , " " } - >
? 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 ) ,
2010-11-16 00:03:09 +01:00
send_kickban_presence1 ( J , Reason , Code , NewAffiliation , StateData )
2003-04-13 21:22:46 +02:00
end , LJIDs ) .
2010-11-16 00:03:09 +01:00
send_kickban_presence1 ( UJID , Reason , Code , Affiliation , StateData ) - >
2011-04-07 12:54:39 +02:00
{ ok , #user { jid = RealJID ,
2003-03-23 21:08:44 +01:00
nick = Nick } } =
? DICT : find ( jlib : jid_tolower ( UJID ) , StateData #state.users ) ,
SAffiliation = affiliation_to_list ( Affiliation ) ,
2011-04-07 12:54:39 +02:00
BannedJIDString = jlib : jid_to_string ( RealJID ) ,
2003-03-23 21:08:44 +01:00
lists : foreach (
2006-01-19 03:17:31 +01:00
fun ( { _ LJID , Info } ) - >
2011-04-07 12:54:39 +02:00
JidAttrList = case ( Info #user.role == moderator ) orelse
( ( StateData #state.config ) #config.anonymous
== false ) of
true - > [ { " jid " , BannedJIDString } ] ;
false - > [ ]
end ,
2003-03-23 21:08:44 +01:00
ItemAttrs = [ { " affiliation " , SAffiliation } ,
2011-04-07 12:54:39 +02:00
{ " role " , " none " } ] ++ JidAttrList ,
2003-03-27 21:55:09 +01:00
ItemEls = case Reason of
" " - >
[ ] ;
_ - >
[ { xmlelement , " reason " , [ ] ,
[ { xmlcdata , Reason } ] } ]
end ,
2003-03-23 21:08:44 +01:00
Packet = { xmlelement , " presence " , [ { " type " , " unavailable " } ] ,
[ { xmlelement , " x " , [ { " xmlns " , ? NS_MUC_USER } ] ,
2003-03-27 21:55:09 +01:00
[ { xmlelement , " item " , ItemAttrs , ItemEls } ,
2003-03-23 21:08:44 +01:00
{ xmlelement , " status " , [ { " code " , Code } ] , [ ] } ] } ] } ,
ejabberd_router : route (
2003-10-07 22:31:44 +02:00
jlib : 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 - >
2006-01-19 03:17:31 +01:00
{ xmlelement , _ Name , _ Attrs , Els } = SubEl ,
2003-03-25 22:03:35 +01:00
case xml : remove_cdata ( Els ) of
2006-01-19 03:17:31 +01:00
[ { xmlelement , " x " , _ Attrs1 , _ Els1 } = XEl ] - >
2003-03-25 22:03:35 +01:00
case { xml : get_tag_attr_s ( " xmlns " , XEl ) ,
xml : get_tag_attr_s ( " type " , XEl ) } of
{ ? NS_XDATA , " cancel " } - >
2003-03-27 21:55:09 +01:00
{ result , [ ] , StateData } ;
2003-03-25 22:03:35 +01:00
{ ? NS_XDATA , " 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:09:05 +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:08:51 +02:00
false - > { error , ? ERR_NOT_ACCEPTABLE }
2009-04-22 14:05:10 +02:00
end ;
2003-03-25 22:03:35 +01:00
_ - >
{ error , ? ERR_BAD_REQUEST }
end ;
2006-01-19 03:17:31 +01:00
[ { xmlelement , " destroy " , _ Attrs1 , _ Els1 } = SubEl1 ] - >
2008-01-16 11:08:17 +01:00
? INFO_MSG ( " Destroyed MUC room ~s by the owner ~s " ,
2007-12-23 13:28:44 +01:00
[ jlib : jid_to_string ( StateData #state.jid ) , jlib : jid_to_string ( From ) ] ) ,
2009-12-29 15:43:24 +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 " ,
{ error , ? ERRT_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 - >
2006-01-19 03:17:31 +01:00
{ xmlelement , _ Name , _ Attrs , Els } = SubEl ,
2003-03-25 22:03:35 +01:00
case xml : remove_cdata ( Els ) of
[ ] - >
2006-03-14 05:26:15 +01:00
get_config ( Lang , StateData , From ) ;
2003-03-28 21:20:49 +01:00
[ Item ] - >
case xml : get_tag_attr ( " affiliation " , Item ) of
false - >
{ error , ? ERR_BAD_REQUEST } ;
{ value , StrAffiliation } - >
case catch list_to_affiliation ( StrAffiliation ) of
{ 'EXIT' , _ } - >
2004-03-02 22:16:55 +01:00
ErrText =
io_lib : format (
translate : translate (
Lang ,
" Invalid affiliation: ~s " ) ,
[ StrAffiliation ] ) ,
{ error , ? ERRT_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
_ - >
{ error , ? ERR_FEATURE_NOT_IMPLEMENTED }
end ;
_ - >
2004-03-02 22:16:55 +01:00
ErrText = " Owner privileges required " ,
{ error , ? ERRT_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:09:05 +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 ) ,
{ xmlelement , " field " , [ { " type " , Type } ,
{ " label " , translate : translate ( Lang , Label ) } ,
{ " var " , Var } ] ,
[ { xmlelement , " value " , [ ] , [ { xmlcdata , Val } ] } ] } ) .
- 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 ) ) .
2011-02-16 11:14:39 +01:00
- define ( JIDMULTIXFIELD ( Label , Var , JIDList ) ,
{ xmlelement , " field " , [ { " type " , " jid-multi " } ,
{ " label " , translate : translate ( Lang , Label ) } ,
{ " var " , Var } ] ,
[ { xmlelement , " value " , [ ] , [ { xmlcdata , jlib : jid_to_string ( JID ) } ] }
| | JID < - JIDList ] } ) .
2008-11-26 16:10:38 +01:00
get_default_room_maxusers ( RoomState ) - >
2008-12-03 16:06:21 +01:00
DefRoomOpts = gen_mod : get_module_opt ( RoomState #state.server_host , mod_muc , default_room_options , [ ] ) ,
2008-11-26 16:10:38 +01:00
RoomState2 = set_opts ( DefRoomOpts , RoomState ) ,
( RoomState2 #state.config ) #config.max_users .
2003-03-25 22:03:35 +01:00
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-11-28 20:51:30 +01:00
DefaultRoomMaxUsers = get_default_room_maxusers ( StateData ) ,
2003-03-25 22:03:35 +01:00
Config = StateData #state.config ,
2008-11-26 16:10:38 +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 =
2004-02-26 23:00:04 +01:00
[ { xmlelement , " title " , [ ] ,
2009-05-26 13:03:45 +02:00
[ { xmlcdata , io_lib : format ( translate : translate ( Lang , " Configuration of room ~s " ) , [ jlib : jid_to_string ( StateData #state.jid ) ] ) } ] } ,
2006-08-04 03:57:51 +02:00
{ xmlelement , " field " , [ { " type " , " hidden " } ,
{ " var " , " FORM_TYPE " } ] ,
[ { xmlelement , " value " , [ ] ,
[ { xmlcdata , " 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-06-28 19:47:19 +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 ) ,
2007-08-28 16:35:50 +02:00
{ xmlelement , " field " ,
[ { " type " , " list-single " } ,
{ " label " , translate : translate ( Lang , " Maximum Number of Occupants " ) } ,
{ " var " , " muc#roomconfig_maxusers " } ] ,
2008-11-26 16:10:38 +01:00
[ { xmlelement , " value " , [ ] , [ { xmlcdata , MaxUsersRoomString } ] } ] ++
2007-08-29 19:54:45 +02:00
if
is_integer ( ServiceMaxUsers ) - > [ ] ;
true - >
[ { xmlelement , " option " ,
[ { " label " , translate : translate ( Lang , " No limit " ) } ] ,
[ { xmlelement , " value " , [ ] , [ { xmlcdata , " none " } ] } ] } ]
end ++
[ { xmlelement , " option " , [ { " label " , erlang : integer_to_list ( N ) } ] ,
[ { xmlelement , " value " , [ ] ,
[ { xmlcdata , erlang : integer_to_list ( N ) } ] } ] } | |
2008-11-26 16:10:38 +01:00
N < - lists : usort ( [ ServiceMaxUsers , DefaultRoomMaxUsers , MaxUsersRoomInteger |
? MAX_USERS_DEFAULT_LIST ] ) , N =< ServiceMaxUsers ]
2007-08-29 19:54:45 +02:00
} ,
2006-08-04 03:57:51 +02:00
{ xmlelement , " field " ,
[ { " type " , " list-single " } ,
2009-01-12 19:41:46 +01:00
{ " label " , translate : translate ( Lang , " Present real Jabber IDs to " ) } ,
2006-08-04 03:57:51 +02:00
{ " var " , " muc#roomconfig_whois " } ] ,
[ { xmlelement , " value " , [ ] , [ { xmlcdata ,
if Config #config.anonymous - >
" moderators " ;
true - >
" anyone "
end } ] } ,
2006-09-24 16:45:12 +02:00
{ xmlelement , " option " , [ { " label " , translate : translate ( Lang , " moderators only " ) } ] ,
2006-08-04 03:57:51 +02:00
[ { xmlelement , " value " , [ ] , [ { xmlcdata , " moderators " } ] } ] } ,
2006-09-24 16:45:12 +02:00
{ xmlelement , " option " , [ { " label " , translate : translate ( Lang , " anyone " ) } ] ,
2006-08-04 03:57:51 +02:00
[ { xmlelement , " value " , [ ] , [ { xmlcdata , " anyone " } ] } ] } ] } ,
2006-01-19 03:17:31 +01:00
? BOOLXFIELD ( " Make room members-only " ,
2006-08-04 03:57:51 +02:00
" muc#roomconfig_membersonly " ,
2006-01-19 03:17:31 +01:00
Config #config.members_only ) ,
2008-02-14 12:25:39 +01:00
? BOOLXFIELD ( " Make room moderated " ,
" muc#roomconfig_moderatedroom " ,
Config #config.moderated ) ,
2006-01-19 03:17:31 +01:00
? 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 " ,
2006-08-04 03:57:51 +02:00
" muc#roomconfig_changesubject " ,
2006-01-19 03:17:31 +01:00
Config #config.allow_change_subj ) ,
? BOOLXFIELD ( " Allow users to send private messages " ,
" allow_private_messages " ,
Config #config.allow_private_messages ) ,
2011-07-11 17:33:26 +02:00
{ xmlelement , " field " ,
[ { " type " , " list-single " } ,
{ " label " , translate : translate ( Lang , " Allow visitors to send private messages to " ) } ,
{ " var " , " allow_private_messages_from_visitors " } ] ,
[ { xmlelement , " value " , [ ] , [ { xmlcdata ,
case Config #config.allow_private_messages_from_visitors of
anyone - >
" anyone " ;
moderators - >
" moderators " ;
nobody - >
" nobody "
end } ] } ,
{ xmlelement , " option " , [ { " label " , translate : translate ( Lang , " nobody " ) } ] ,
[ { xmlelement , " value " , [ ] , [ { xmlcdata , " nobody " } ] } ] } ,
{ xmlelement , " option " , [ { " label " , translate : translate ( Lang , " moderators only " ) } ] ,
[ { xmlelement , " value " , [ ] , [ { xmlcdata , " moderators " } ] } ] } ,
{ xmlelement , " option " , [ { " label " , translate : translate ( Lang , " anyone " ) } ] ,
[ { xmlelement , " value " , [ ] , [ { xmlcdata , " anyone " } ] } ] } ] } ,
2006-01-19 03:17:31 +01:00
? BOOLXFIELD ( " Allow users to query other users " ,
" allow_query_users " ,
Config #config.allow_query_users ) ,
? BOOLXFIELD ( " Allow users to send invites " ,
2006-08-04 03:57:51 +02:00
" muc#roomconfig_allowinvites " ,
2008-07-23 14:31:55 +02:00
Config #config.allow_user_invites ) ,
2008-07-23 19:45:23 +02:00
? BOOLXFIELD ( " Allow visitors to send status text in presence updates " ,
2008-07-23 18:55:46 +02:00
" muc#roomconfig_allowvisitorstatus " ,
Config #config.allow_visitor_status ) ,
2008-07-23 14:31:55 +02:00
? BOOLXFIELD ( " Allow visitors to change nickname " ,
" muc#roomconfig_allowvisitornickchange " ,
Config #config.allow_visitor_nickchange )
2006-03-14 05:26:15 +01:00
] ++
2009-05-26 13:53:58 +02:00
case ejabberd_captcha : is_feature_available ( ) of
true - >
[ ? BOOLXFIELD ( " Make room captcha protected " ,
" captcha_protected " ,
Config #config.captcha_protected ) ] ;
false - > [ ]
end ++
2011-02-16 11:14:39 +01:00
[ ? JIDMULTIXFIELD ( " Exclude Jabber IDs from CAPTCHA challenge " ,
" muc#roomconfig_captcha_whitelist " ,
? SETS : to_list ( Config #config.captcha_whitelist ) ) ] ++
2006-03-14 05:26:15 +01:00
case mod_muc_log : check_access_log (
StateData #state.server_host , From ) of
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 ,
2004-02-26 23:00:04 +01:00
{ result , [ { xmlelement , " instructions " , [ ] ,
[ { xmlcdata ,
translate : translate (
2007-08-28 16:35:50 +02:00
Lang , " You need an x:data capable client to configure room " ) } ] } ,
2004-12-30 00:10:14 +01:00
{ xmlelement , " x " , [ { " xmlns " , ? NS_XDATA } ,
{ " type " , " form " } ] ,
Res } ] ,
StateData } .
2003-03-25 22:03:35 +01:00
set_config ( XEl , StateData ) - >
XData = jlib : parse_xdata_submit ( XEl ) ,
case XData of
invalid - >
{ error , ? ERR_BAD_REQUEST } ;
_ - >
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:33:56 +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 } ) ;
2003-03-25 22:03:35 +01:00
_ - > { error , ? ERR_BAD_REQUEST }
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 } ) ;
_ - >
{ error , ? ERR_BAD_REQUEST }
end ) .
2003-03-27 21:55:09 +01:00
- define ( SET_STRING_XOPT ( Opt , Val ) ,
set_xoption ( Opts , Config #config { Opt = Val } ) ) .
2011-02-16 11:14:39 +01:00
- define ( SET_JIDMULTI_XOPT ( Opt , Vals ) ,
begin
Set = lists : foldl (
fun ( { U , S , R } , Set1 ) - >
? SETS : add_element ( { U , S , R } , Set1 ) ;
( #jid { luser = U , lserver = S , lresource = R } , Set1 ) - >
? SETS : add_element ( { U , S , R } , Set1 ) ;
( _ , Set1 ) - >
Set1
end , ? SETS : empty ( ) , Vals ) ,
set_xoption ( Opts , Config #config { Opt = Set } )
end ) .
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-06-28 19:47:19 +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 ) ;
2011-07-11 17:33:26 +02:00
set_xoption ( [ { " allow_private_messages_from_visitors " , [ Val ] } | Opts ] , Config ) - >
case Val of
" anyone " - >
? SET_STRING_XOPT ( allow_private_messages_from_visitors , anyone ) ;
" moderators " - >
? SET_STRING_XOPT ( allow_private_messages_from_visitors , moderators ) ;
" nobody " - >
? SET_STRING_XOPT ( allow_private_messages_from_visitors , nobody ) ;
_ - >
{ error , ? ERR_BAD_REQUEST }
end ;
2008-07-23 18:55:46 +02:00
set_xoption ( [ { " muc#roomconfig_allowvisitorstatus " , [ Val ] } | Opts ] , Config ) - >
? SET_BOOL_XOPT ( allow_visitor_status , Val ) ;
2008-07-23 14:31:55 +02:00
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-03-13 17:01:46 +01: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 " - >
2011-09-05 09:33:51 +02:00
? SET_BOOL_XOPT ( anonymous , integer_to_list ( 1 ) ) ;
2006-08-04 03:57:51 +02:00
" anyone " - >
2011-09-05 09:33:51 +02:00
? SET_BOOL_XOPT ( anonymous , integer_to_list ( 0 ) ) ;
2006-08-04 03:57:51 +02:00
_ - >
{ error , ? ERR_BAD_REQUEST }
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 ) ;
2011-02-16 11:14:39 +01:00
set_xoption ( [ { " muc#roomconfig_captcha_whitelist " , Vals } | Opts ] , Config ) - >
JIDs = [ jlib : string_to_jid ( Val ) | | Val < - Vals ] ,
? SET_JIDMULTI_XOPT ( captcha_whitelist , JIDs ) ;
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 ) - >
2003-03-25 22:03:35 +01:00
{ error , ? ERR_BAD_REQUEST } .
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-06-28 19:47:19 +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 } } ;
2011-07-11 17:33:26 +02:00
allow_private_messages_from_visitors - > StateData #state { config = ( StateData #state.config ) #config { allow_private_messages_from_visitors = Val } } ;
2008-07-23 14:31:55 +02:00
allow_visitor_nickchange - > StateData #state { config = ( StateData #state.config ) #config { allow_visitor_nickchange = Val } } ;
2008-07-23 18:55:46 +02:00
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-03-13 17:01:46 +01: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 } } ;
2011-02-16 11:14:39 +01:00
captcha_whitelist - > StateData #state { config = ( StateData #state.config ) #config { captcha_whitelist = ? SETS : from_list ( 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-06-28 19:47:19 +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 ) ,
2011-07-11 17:33:26 +02:00
? MAKE_CONFIG_OPT ( allow_private_messages_from_visitors ) ,
2008-07-23 18:55:46 +02:00
? MAKE_CONFIG_OPT ( allow_visitor_status ) ,
2008-07-23 14:31:55 +02:00
? 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-03-13 17:01:46 +01: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 ) ,
2011-02-16 11:14:39 +01:00
{ captcha_whitelist ,
? SETS : to_list ( ( StateData #state.config ) #config.captcha_whitelist ) } ,
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 ,
ItemAttrs = [ { " affiliation " , " none " } ,
{ " role " , " none " } ] ,
Packet = { xmlelement , " presence " , [ { " type " , " unavailable " } ] ,
[ { xmlelement , " x " , [ { " xmlns " , ? NS_MUC_USER } ] ,
2006-01-19 03:17:31 +01:00
[ { xmlelement , " item " , ItemAttrs , [ ] } , DEl ] } ] } ,
2003-03-27 21:55:09 +01:00
ejabberd_router : route (
2003-10-07 22:31:44 +02:00
jlib : 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
2004-01-17 21:26:57 +01:00
- define ( FEATURE ( Var ) , { xmlelement , " feature " , [ { " var " , Var } ] , [ ] } ) .
- 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 ) - >
2003-03-27 15:06:17 +01:00
{ error , ? ERR_NOT_ALLOWED } ;
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 ,
2003-05-07 15:18:09 +02:00
{ result , [ { xmlelement , " identity " ,
[ { " category " , " conference " } ,
{ " type " , " text " } ,
{ " name " , get_title ( StateData ) } ] , [ ] } ,
{ xmlelement , " feature " ,
2004-01-17 21:26:57 +01:00
[ { " var " , ? NS_MUC } ] , [ ] } ,
? 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 ) ,
{ xmlelement , " field " , [ { " type " , Type } , { " var " , Var } ] ,
[ { xmlelement , " value " , [ ] , [ { xmlcdata , Val } ] } ] } ) .
2003-03-27 15:06:17 +01:00
2006-01-19 03:17:31 +01:00
- define ( RFIELD ( Label , Var , Val ) ,
{ xmlelement , " field " , [ { " label " , translate : translate ( Lang , Label ) } ,
{ " var " , Var } ] ,
[ { xmlelement , " value " , [ ] , [ { xmlcdata , Val } ] } ] } ) .
2003-03-27 15:06:17 +01:00
2006-01-19 03:17:31 +01:00
iq_disco_info_extras ( Lang , StateData ) - >
2009-08-21 15:22:18 +02:00
Len = ? DICT : size ( StateData #state.users ) ,
2008-06-28 19:47:19 +02:00
RoomDescription = ( StateData #state.config ) #config.description ,
2006-01-19 03:17:31 +01:00
[ { xmlelement , " x " , [ { " xmlns " , ? NS_XDATA } , { " type " , " result " } ] ,
[ ? RFIELDT ( " hidden " , " FORM_TYPE " ,
" http://jabber.org/protocol/muc#roominfo " ) ,
2008-06-28 19:47:19 +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 ) - >
2003-03-27 15:06:17 +01:00
{ error , ? ERR_NOT_ALLOWED } ;
2006-01-19 03:17:31 +01:00
process_iq_disco_items ( From , get , _ Lang , StateData ) - >
2010-03-29 20:51:24 +02:00
case ( StateData #state.config ) #config.public_list of
true - >
{ result , get_mucroom_disco_items ( StateData ) , StateData } ;
_ - >
case is_occupant_or_admin ( From , StateData ) of
true - >
{ result , get_mucroom_disco_items ( StateData ) , StateData } ;
_ - >
{ error , ? ERR_FORBIDDEN }
end
end .
2003-03-27 15:06:17 +01:00
2009-03-13 17:01:46 +01:00
process_iq_captcha ( _ From , get , _ Lang , _ SubEl , _ StateData ) - >
{ error , ? ERR_NOT_ALLOWED } ;
process_iq_captcha ( _ From , set , _ Lang , SubEl , StateData ) - >
case ejabberd_captcha : process_reply ( SubEl ) of
ok - >
{ result , [ ] , StateData } ;
_ - >
{ error , ? ERR_NOT_ACCEPTABLE }
end .
2003-03-27 21:55:09 +01:00
get_title ( StateData ) - >
case ( StateData #state.config ) #config.title of
" " - >
StateData #state.room ;
Name - >
Name
end .
2010-06-23 09:44:35 +02:00
get_roomdesc_reply ( JID , StateData , Tail ) - >
IsOccupantOrAdmin = is_occupant_or_admin ( JID , StateData ) ,
if ( StateData #state.config ) #config.public or IsOccupantOrAdmin - >
if ( StateData #state.config ) #config.public_list or IsOccupantOrAdmin - >
{ item , get_title ( StateData ) ++ Tail } ;
true - >
{ item , get_title ( StateData ) }
end ;
true - >
2010-03-29 20:51:24 +02:00
false
end .
2010-03-29 20:49:52 +02:00
get_roomdesc_tail ( StateData , Lang ) - >
2010-03-29 20:51:24 +02:00
Desc = case ( StateData #state.config ) #config.public of
true - >
" " ;
_ - >
translate : translate ( Lang , " private, " )
end ,
Len = ? DICT : fold ( fun ( _ , _ , Acc ) - > Acc + 1 end , 0 , StateData #state.users ) ,
" ( " ++ Desc ++ integer_to_list ( Len ) ++ " ) " .
2010-03-29 20:49:52 +02:00
get_mucroom_disco_items ( StateData ) - >
2010-03-29 20:51:24 +02:00
lists : map (
fun ( { _ LJID , Info } ) - >
Nick = Info #user.nick ,
{ xmlelement , " item " ,
[ { " jid " , jlib : jid_to_string ( { StateData #state.room ,
StateData #state.host , Nick } ) } ,
{ " name " , Nick } ] , [ ] }
end ,
? DICT : to_list ( StateData #state.users ) ) .
2003-03-27 15:06:17 +01:00
2011-07-23 16:13:27 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Voice request support
is_voice_request ( Els ) - >
try
case xml : remove_cdata ( Els ) of
[ { xmlelement , " x " , _ , Els1 } = XEl ] - >
2011-07-24 11:46:47 +02:00
case xml : get_tag_attr_s ( " xmlns " , XEl ) of
2011-07-23 16:13:27 +02:00
? NS_XDATA - >
2011-07-24 11:46:47 +02:00
lists : foldl (
fun ( X , Y ) - >
check_voice_request_fields ( X , Y )
end ,
true , xml : remove_cdata ( Els1 ) )
2011-07-23 16:13:27 +02:00
end
end
catch
2011-07-24 13:57:13 +02:00
error : _ - >
2011-07-23 16:13:27 +02:00
false
end .
check_voice_request_fields ( { xmlelement , " field " , _ , Els } = Elem , Acc ) - >
try
case Acc of
true - >
2011-07-24 11:46:47 +02:00
case xml : get_tag_attr_s ( " var " , Elem ) of
2011-07-23 16:13:27 +02:00
" FORM_TYPE " - >
2011-07-24 11:46:47 +02:00
[ { xmlelement , " value " , _ , Value } ] = xml : remove_cdata ( Els ) ,
2011-07-23 16:13:27 +02:00
case xml : get_cdata ( Value ) of
" http://jabber.org/protocol/muc#request " - >
true
end ;
" muc#role " - >
2011-07-24 11:46:47 +02:00
[ { xmlelement , " value " , _ , Value } ] = xml : remove_cdata ( Els ) ,
2011-07-23 16:13:27 +02:00
case xml : get_cdata ( Value ) of
" participant " - >
true
end
end
end
catch
2011-07-24 13:57:13 +02:00
error : _ - >
2011-07-23 16:13:27 +02:00
false
end .
2011-07-24 11:46:47 +02:00
prepare_request_form ( Requester , Nick , Lang ) - >
{ xmlelement , " message " , [ { " type " , " normal " } ] , [
{ xmlelement , " x " , [ { " xmlns " , ? NS_XDATA } , { " type " , " form " } ] ,
[
{ xmlelement , " title " , [ ] ,
[ { xmlcdata , translate : translate ( Lang , " Voice request " ) } ] } ,
{ xmlelement , " instructions " , [ ] ,
2011-07-24 14:25:53 +02:00
[ { xmlcdata , translate : translate ( Lang , " To approve this request for voice, select the \" Grant voice to this person? \" checkbox and click OK. To skip this request, click the cancel button. " ) } ] } ,
2011-07-24 11:46:47 +02:00
{ xmlelement , " field " , [ { " var " , " FORM_TYPE " } , { " type " , " hidden " } ] ,
[ { xmlelement , " value " , [ ] ,
[ { xmlcdata , " http://jabber.org/protocol/muc#request " } ] } ] } ,
? STRINGXFIELD ( " Requested role " , " muc#role " , " participant " ) ,
? STRINGXFIELD ( " User JID " , " muc#jid " , jlib : jid_to_string ( Requester ) ) ,
? STRINGXFIELD ( " Nickname " , " muc#roomnick " , Nick ) ,
? BOOLXFIELD ( " Grant voice to this person? " , " muc#request_allow " , false )
]
} ] } .
send_voice_request ( From , StateData ) - >
Moderators = search_role ( moderator , StateData ) ,
[ { _ , #user { nick = FromNick } } ] = lists : filter (
fun ( { _ , #user { jid = Jid } } ) - > Jid == From end ,
? DICT : to_list ( StateData #state.users ) ) ,
lists : map (
2011-07-24 14:06:12 +02:00
fun ( { _ , User } ) - >
ejabberd_router : route (
StateData #state.jid ,
User #user.jid ,
prepare_request_form ( From , FromNick , " " ) )
2011-07-24 11:46:47 +02:00
end , Moderators ) ,
ok .
2011-07-25 18:09:11 +02:00
is_voice_approvement ( Els ) - >
try
case xml : remove_cdata ( Els ) of
[ { xmlelement , " x " , Attrs , Els1 } ] - >
case xml : get_attr_s ( " xmlns " , Attrs ) of
? NS_XDATA - >
case xml : get_attr_s ( " type " , Attrs ) of
" submit " - >
lists : foldl (
fun ( X , Y ) - >
check_voice_approvement_fields ( X , Y )
end ,
true , xml : remove_cdata ( Els1 ) )
end
end
end
catch
error : _ - >
false
end .
check_voice_approvement_fields ( { xmlelement , " field " , Attrs , Els } , Acc ) - >
if Acc - >
case xml : get_attr_s ( " var " , Attrs ) of
" FORM_TYPE " - >
[ { xmlelement , " value " , _ , Value } ] = xml : remove_cdata ( Els ) ,
case xml : get_cdata ( Value ) of
" http://jabber.org/protocol/muc#request " - >
true
end ;
" muc#role " - >
[ { xmlelement , " value " , _ , Value } ] = xml : remove_cdata ( Els ) ,
case xml : get_cdata ( Value ) of
" participant " - >
true
end ;
" muc#jid " - >
true ; % TODO: make some validation here
" muc#roomnick " - >
true ;
" muc#request_allow " - >
% XXX: submitted forms with request_allow unchecked ignored here
[ { xmlelement , " value " , _ , Value } ] = xml : remove_cdata ( Els ) ,
case xml : get_cdata ( Value ) of
" true " - >
true ;
" 1 " - >
true
end
end
end .
2011-07-25 21:46:59 +02:00
extract_jid_from_voice_approvement ( Els ) - >
try
[ { xmlelement , " x " , _ , Els1 } ] = xml : remove_cdata ( Els ) ,
Jid = lists : foldl (
fun ( El , Acc ) - >
case xml : get_tag_attr_s ( " var " , El ) of
" muc#jid " - >
{ xmlelement , " field " , _ , Els2 } = El ,
[ { xmlelement , " value " , _ , Value } ] = xml : remove_cdata ( Els2 ) ,
xml : get_cdata ( Value ) ;
_ - >
Acc
end
end , { error , jid_not_found } , xml : remove_cdata ( Els1 ) ) ,
case Jid of
{ error , _ } = Err - >
Err ;
_ - >
jlib : string_to_jid ( Jid )
end
catch
error : X - >
{ error , X }
end .
2003-04-13 21:22:46 +02:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Invitation support
2011-07-23 11:16:17 +02:00
is_invitation ( Els ) - >
2011-07-24 14:01:04 +02:00
try
case xml : remove_cdata ( Els ) of
[ { xmlelement , " x " , _ Attrs1 , Els1 } = XEl ] - >
case xml : get_tag_attr_s ( " xmlns " , XEl ) of
? NS_MUC_USER - >
case xml : remove_cdata ( Els1 ) of
[ { xmlelement , " invite " , _ , _ } ] - >
true
end
end
end
catch
error : _ - >
2011-07-23 11:16:17 +02:00
false
end .
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 ) ,
2007-05-03 06:07:29 +02:00
InviteEl = case xml : remove_cdata ( Els ) of
[ { xmlelement , " x " , _ Attrs1 , Els1 } = XEl ] - >
case xml : get_tag_attr_s ( " xmlns " , XEl ) of
? NS_MUC_USER - >
ok ;
_ - >
throw ( { error , ? ERR_BAD_REQUEST } )
end ,
case xml : remove_cdata ( Els1 ) of
[ { xmlelement , " invite " , _ Attrs2 , _ Els2 } = InviteEl1 ] - >
InviteEl1 ;
_ - >
throw ( { error , ? ERR_BAD_REQUEST } )
end ;
_ - >
throw ( { error , ? ERR_BAD_REQUEST } )
end ,
JID = case jlib : string_to_jid (
xml : get_tag_attr_s ( " to " , InviteEl ) ) of
error - >
throw ( { error , ? ERR_JID_MALFORMED } ) ;
JID1 - >
JID1
end ,
case CanInvite of
false - >
throw ( { error , ? ERR_NOT_ALLOWED } ) ;
true - >
Reason =
xml : get_path_s (
InviteEl ,
[ { elem , " reason " } , cdata ] ) ,
2008-01-01 18:06:26 +01:00
ContinueEl =
case xml : get_path_s (
InviteEl ,
[ { elem , " continue " } ] ) of
[ ] - > [ ] ;
Continue1 - > [ Continue1 ]
end ,
2007-05-03 06:07:29 +02:00
IEl =
[ { xmlelement , " invite " ,
[ { " from " ,
jlib : jid_to_string ( From ) } ] ,
[ { xmlelement , " reason " , [ ] ,
2008-01-01 18:06:26 +01:00
[ { xmlcdata , 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 - >
[ { xmlelement , " password " , [ ] ,
[ { xmlcdata , ( StateData #state.config ) #config.password } ] } ] ;
_ - >
[ ]
end ,
2007-06-26 20:00:36 +02:00
Body =
{ xmlelement , " body " , [ ] ,
[ { xmlcdata ,
lists : flatten (
io_lib : format (
translate : translate (
Lang ,
2007-12-05 10:09:09 +01:00
" ~s invites you to the room ~s " ) ,
[ jlib : jid_to_string ( From ) ,
jlib : jid_to_string ( { StateData #state.room ,
2007-06-26 20:00:36 +02:00
StateData #state.host ,
2007-12-05 10:09:09 +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 ++ " ' " ;
_ - >
" "
end ++
case Reason of
" " - > " " ;
_ - > " ( " ++ Reason ++ " ) "
end
} ] } ,
2007-05-03 06:07:29 +02:00
Msg =
{ xmlelement , " message " ,
[ { " type " , " normal " } ] ,
[ { xmlelement , " x " , [ { " xmlns " , ? NS_MUC_USER } ] , IEl ++ PasswdEl } ,
{ xmlelement , " x " ,
[ { " xmlns " , ? NS_XCONFERENCE } ,
{ " jid " , jlib : jid_to_string (
{ StateData #state.room ,
StateData #state.host ,
" " } ) } ] ,
2007-06-26 20:00:36 +02:00
[ { xmlcdata , 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-02-11 13:15:34 +01:00
{ xmlelement , " message " , _ , _ } = Packet ,
2008-02-06 21:30:58 +01:00
XEl = xml : get_subtag ( Packet , " x " ) ,
? NS_MUC_USER = xml : get_tag_attr_s ( " xmlns " , XEl ) ,
DEl = xml : get_subtag ( XEl , " decline " ) ,
ToString = xml : get_tag_attr_s ( " to " , DEl ) ,
ToJID = jlib : string_to_jid ( 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-02-11 13:15:34 +01:00
send_decline_invitation ( { Packet , XEl , DEl , ToJID } , RoomJID , FromJID ) - >
FromString = jlib : jid_to_string ( FromJID ) ,
2008-02-06 21:30:58 +01:00
{ xmlelement , " decline " , DAttrs , DEls } = DEl ,
DAttrs2 = lists : keydelete ( " to " , 1 , DAttrs ) ,
DAttrs3 = [ { " from " , FromString } | DAttrs2 ] ,
DEl2 = { xmlelement , " decline " , DAttrs3 , DEls } ,
XEl2 = replace_subelement ( XEl , DEl2 ) ,
Packet2 = replace_subelement ( Packet , XEl2 ) ,
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.
replace_subelement ( { xmlelement , Name , Attrs , SubEls } , NewSubEl ) - >
{ _ , NameNewSubEl , _ , _ } = NewSubEl ,
SubEls2 = lists : keyreplace ( NameNewSubEl , 2 , SubEls , NewSubEl ) ,
{ xmlelement , Name , Attrs , SubEls2 } .
send_error_only_occupants ( Packet , Lang , RoomJID , From ) - >
ErrText = " Only occupants are allowed to send messages to the conference " ,
Err = jlib : make_error_reply ( Packet , ? ERRT_NOT_ACCEPTABLE ( Lang , ErrText ) ) ,
ejabberd_router : route ( RoomJID , From , Err ) .
2006-03-14 05:26:15 +01:00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Logging
2009-07-21 20:33:56 +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 ) - >
2011-08-23 21:53:41 +02:00
{ LUser , LServer , LResource } = jlib : jid_tolower ( 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 ,
2011-08-23 21:53:41 +02:00
#muc_online_users { us = US , resource = LResource , room = Room , host = Host } ) .
2007-12-03 11:47:42 +01:00
tab_remove_online_user ( JID , StateData ) - >
2011-08-23 21:53:41 +02:00
{ LUser , LServer , LResource } = jlib : jid_tolower ( JID ) ,
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 ,
2011-08-23 21:53:41 +02:00
#muc_online_users { us = US , resource = LResource , room = Room , host = Host } ) .
2007-12-03 11:47:42 +01:00
tab_count_user ( JID ) - >
{ LUser , LServer , _ } = jlib : jid_tolower ( JID ) ,
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 .
2010-07-01 12:54:01 +02:00
element_size ( El ) - >
size ( xml : element_to_binary ( El ) ) .