summaryrefslogtreecommitdiff
path: root/src/mod_muc
diff options
context:
space:
mode:
authorAlexey Shchepin <alexey@process-one.net>2003-03-25 21:03:35 +0000
committerAlexey Shchepin <alexey@process-one.net>2003-03-25 21:03:35 +0000
commit6d89957e06cedf4856112151adbc14bedbc594b3 (patch)
tree00533538a5289df8b7c24f6c4aeeec8fa7f30af9 /src/mod_muc
parent* src/mod_muc/: MUC support (not completed yet) (diff)
* src/mod_muc/: Support for more configuration options and
persistent rooms SVN Revision: 91
Diffstat (limited to 'src/mod_muc')
-rw-r--r--src/mod_muc/mod_muc.erl44
-rw-r--r--src/mod_muc/mod_muc_room.erl338
2 files changed, 369 insertions, 13 deletions
diff --git a/src/mod_muc/mod_muc.erl b/src/mod_muc/mod_muc.erl
index 84a0b8e1..b94a948d 100644
--- a/src/mod_muc/mod_muc.erl
+++ b/src/mod_muc/mod_muc.erl
@@ -15,7 +15,10 @@
-export([start/1,
init/1,
stop/0,
- room_destroyed/1]).
+ room_destroyed/1,
+ store_room/2,
+ restore_room/1,
+ forget_room/1]).
-include("ejabberd.hrl").
-include("jlib.hrl").
@@ -38,7 +41,7 @@ init(Host) ->
public,
{keypos, #muc_online_room.name}]),
ejabberd_router:register_route(Host),
- % TODO: load permanent groups
+ load_permanent_rooms(Host),
loop(Host).
loop(Host) ->
@@ -136,7 +139,44 @@ iq_disco() ->
[{"var", ?NS_MUC}], []}].
+store_room(Name, Opts) ->
+ F = fun() ->
+ mnesia:write(#muc_room{name = Name,
+ opts = Opts})
+ end,
+ mnesia:transaction(F).
+
+restore_room(Name) ->
+ case catch mnesia:dirty_read(muc_room, Name) of
+ [#muc_room{opts = Opts}] ->
+ Opts;
+ _ ->
+ error
+ end.
+forget_room(Name) ->
+ F = fun() ->
+ mnesia:delete({muc_room, Name})
+ end,
+ mnesia:transaction(F).
+load_permanent_rooms(Host) ->
+ case catch mnesia:dirty_select(muc_room, [{'_', [], ['$_']}]) of
+ {'EXIT', Reason} ->
+ ?ERROR_MSG("~p", [Reason]),
+ ok;
+ Rs ->
+ lists:foreach(fun(R) ->
+ Room = R#muc_room.name,
+ {ok, Pid} = mod_muc_room:start(
+ Host,
+ Room,
+ R#muc_room.opts),
+ ets:insert(
+ muc_online_room,
+ #muc_online_room{name = Room, pid = Pid})
+ end, Rs)
+ end.
+
diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl
index 22d3fc9f..e01dab87 100644
--- a/src/mod_muc/mod_muc_room.erl
+++ b/src/mod_muc/mod_muc_room.erl
@@ -15,7 +15,7 @@
%% External exports
-export([start/4,
- init/1,
+ start/3,
route/4]).
%% gen_fsm callbacks
@@ -35,6 +35,20 @@
-record(lqueue, {queue, len, max}).
+-record(config, {allow_change_subj = true, % TODO
+ allow_query_users = true,
+ allow_private_messages = true,
+ public = true, % TODO
+ persistent = false, % TODO
+ moderated = false, % TODO
+ members_by_default = true, % TODO
+ members_only = false, % TODO
+ allow_user_invites = false, % TODO
+ password_protected = false, % TODO
+ anonymous = true, % TODO
+ logging = false % TODO
+ }).
+
-record(user, {jid,
nick,
role,
@@ -42,7 +56,7 @@
-record(state, {room,
host,
- config,
+ config = #config{},
users = ?DICT:new(),
affiliations = ?DICT:new(),
history = lqueue_new(10),
@@ -78,6 +92,9 @@
start(Host, Room, Creator, Nick) ->
gen_fsm:start(?MODULE, [Host, Room, Creator, Nick], ?FSMOPTS).
+start(Host, Room, Opts) ->
+ gen_fsm:start(?MODULE, [Host, Room, Opts], ?FSMOPTS).
+
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
%%%----------------------------------------------------------------------
@@ -94,6 +111,10 @@ init([Host, Room, Creator, Nick]) ->
State = set_affiliation(Creator, owner,
#state{host = Host,
room = Room}),
+ {ok, normal_state, State};
+init([Host, Room, Opts]) ->
+ State = set_opts(Opts, #state{host = Host,
+ room = Room}),
{ok, normal_state, State}.
%%----------------------------------------------------------------------
@@ -186,14 +207,21 @@ normal_state({route, From, "",
{xmlelement, "iq", Attrs, Els} = Packet},
StateData) ->
case jlib:iq_query_info(Packet) of
- {iq, ID, Type, ?NS_MUC_ADMIN = XMLNS, SubEl} ->
+ {iq, ID, Type, XMLNS, SubEl} when
+ (XMLNS == ?NS_MUC_ADMIN) or (XMLNS == ?NS_MUC_OWNER) ->
+ Res1 = case XMLNS of
+ ?NS_MUC_ADMIN ->
+ process_iq_admin(From, Type, SubEl, StateData);
+ ?NS_MUC_OWNER ->
+ process_iq_owner(From, Type, SubEl, StateData)
+ end,
{IQRes, NewStateData} =
- case process_iq_admin(From, Type, SubEl, StateData) of
+ case Res1 of
{result, Res, SD} ->
{{iq, ID, result, XMLNS,
[{xmlelement, "query", [{"xmlns", XMLNS}],
Res
- }]},
+ }]},
SD};
{error, Error} ->
{{iq, ID, error, XMLNS,
@@ -314,7 +342,8 @@ normal_state({route, From, Nick,
normal_state({route, From, ToNick,
{xmlelement, "message", Attrs, Els} = Packet},
StateData) ->
- case is_user_online(From, StateData) of
+ case (StateData#state.config)#config.allow_private_messages
+ andalso is_user_online(From, StateData) of
true ->
case find_jid_by_nick(ToNick, StateData) of
false ->
@@ -342,10 +371,30 @@ normal_state({route, From, ToNick,
normal_state({route, From, ToNick,
{xmlelement, "iq", Attrs, Els} = Packet},
StateData) ->
- Err = jlib:make_error_reply(
- Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
- ejabberd_router:route(
- {StateData#state.room, StateData#state.host, ToNick}, From, Err),
+ case (StateData#state.config)#config.allow_query_users
+ andalso is_user_online(From, StateData) of
+ true ->
+ case find_jid_by_nick(ToNick, StateData) of
+ false ->
+ Err = jlib:make_error_reply(
+ Packet, ?ERR_JID_NOT_FOUND),
+ ejabberd_router:route(
+ {StateData#state.room, StateData#state.host, ToNick},
+ From, Err);
+ ToJID ->
+ {ok, #user{nick = FromNick}} =
+ ?DICT:find(jlib:jid_tolower(From),
+ StateData#state.users),
+ ejabberd_router:route(
+ {StateData#state.room, StateData#state.host, FromNick},
+ ToJID, Packet)
+ end;
+ _ ->
+ Err = jlib:make_error_reply(
+ Packet, ?ERR_NOT_ALLOWED),
+ ejabberd_router:route(
+ {StateData#state.room, StateData#state.host, ToNick}, From, Err)
+ end,
{next_state, normal_state, StateData};
normal_state(Event, StateData) ->
@@ -501,7 +550,13 @@ get_default_role(Affiliation, StateData) ->
admin -> moderator;
member -> participant;
outcast -> none;
- none -> participant
+ none ->
+ case (StateData#state.config)#config.members_by_default of
+ true ->
+ participant;
+ _ ->
+ visitor
+ end
end.
@@ -1184,4 +1239,265 @@ send_kickban_presence(UJID, Code, StateData) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Owner stuff
+
+process_iq_owner(From, set, SubEl, StateData) ->
+ FAffiliation = get_affiliation(From, StateData),
+ case FAffiliation of
+ owner ->
+ {xmlelement, Name, Attrs, Els} = SubEl,
+ Lang = xml:get_tag_attr_s("xml:lang", SubEl),
+ case xml:remove_cdata(Els) of
+ [{xmlelement, "x", Attrs1, Els1} = XEl] ->
+ case {xml:get_tag_attr_s("xmlns", XEl),
+ xml:get_tag_attr_s("type", XEl)} of
+ {?NS_XDATA, "cancel"} ->
+ {error, ?ERR_FEATURE_NOT_IMPLEMENTED};
+ {?NS_XDATA, "submit"} ->
+ set_config(XEl, StateData);
+ _ ->
+ {error, ?ERR_BAD_REQUEST}
+ end;
+ _ ->
+ {error, ?ERR_FEATURE_NOT_IMPLEMENTED}
+ end;
+ _ ->
+ {error, ?ERR_NOT_ALLOWED}
+ end;
+% {xmlelement, _, _, Items} = SubEl,
+% process_admin_items_set(From, Items, StateData);
+% {error, ?ERR_FEATURE_NOT_IMPLEMENTED};
+
+process_iq_owner(From, get, SubEl, StateData) ->
+ FAffiliation = get_affiliation(From, StateData),
+ case FAffiliation of
+ owner ->
+ {xmlelement, Name, Attrs, Els} = SubEl,
+ Lang = xml:get_tag_attr_s("xml:lang", SubEl),
+ case xml:remove_cdata(Els) of
+ [] ->
+ get_config(Lang, StateData);
+ _ ->
+ {error, ?ERR_FEATURE_NOT_IMPLEMENTED}
+ end;
+ _ ->
+ {error, ?ERR_NOT_ALLOWED}
+ end.
+
+
+% 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 ->
+% Items = items_with_affiliation(
+% SAffiliation, StateData),
+% {result, Items, StateData};
+% true ->
+% {error, ?ERR_NOT_ALLOWED}
+% end
+% end
+% end;
+% {value, StrRole} ->
+% case catch list_to_role(StrRole) of
+% {'EXIT', _} ->
+% {error, ?ERR_BAD_REQUEST};
+% SRole ->
+% if
+% FAffiliation == owner ->
+% Items = items_with_role(SRole, StateData),
+% {result, Items, StateData};
+% true ->
+% {error, ?ERR_NOT_ALLOWED}
+% end
+% end
+% end
+% end.
+
+
+-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)).
+
+
+get_config(Lang, StateData) ->
+ Config = StateData#state.config,
+ Res =
+ [?BOOLXFIELD("Allow users to change subject?",
+ "allow_change_subj",
+ Config#config.allow_change_subj),
+ ?BOOLXFIELD("Allow users to query other users?",
+ "allow_query_users",
+ Config#config.allow_query_users),
+ ?BOOLXFIELD("Allow users to send private messages?",
+ "allow_private_messages",
+ Config#config.allow_private_messages),
+ ?BOOLXFIELD("Make room public searchable?",
+ "public",
+ Config#config.public),
+ ?BOOLXFIELD("Make room persistent?",
+ "persistent",
+ Config#config.persistent),
+ ?BOOLXFIELD("Make room moderated?",
+ "moderated",
+ Config#config.moderated),
+ ?BOOLXFIELD("Default users as members?",
+ "members_by_default",
+ Config#config.members_by_default),
+ ?BOOLXFIELD("Make room members only?",
+ "members_only",
+ Config#config.members_only),
+ ?BOOLXFIELD("Allow users to send invites?",
+ "allow_user_invites",
+ Config#config.allow_user_invites),
+ ?BOOLXFIELD("Make room password protected?",
+ "password_protected",
+ Config#config.password_protected),
+ ?BOOLXFIELD("Make room anonymous?",
+ "anonymous",
+ Config#config.anonymous),
+ ?BOOLXFIELD("Enable logging?",
+ "logging",
+ Config#config.logging)
+ ],
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}], Res}], StateData}.
+
+
+
+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 ->
+ change_config(Config, StateData);
+ Err ->
+ Err
+ end
+ end.
+
+-define(SET_BOOL_XOPT(Opt, Val),
+ case Val of
+ "0" -> set_xoption(Opts, Config#config{Opt = false});
+ "1" -> set_xoption(Opts, Config#config{Opt = true});
+ _ -> {error, ?ERR_BAD_REQUEST}
+ end).
+
+
+
+set_xoption([], Config) ->
+ Config;
+set_xoption([{"allow_change_subj", [Val]} | Opts], Config) ->
+ ?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);
+set_xoption([{"public", [Val]} | Opts], Config) ->
+ ?SET_BOOL_XOPT(public, Val);
+set_xoption([{"persistent", [Val]} | Opts], Config) ->
+ ?SET_BOOL_XOPT(persistent, Val);
+set_xoption([{"moderated", [Val]} | Opts], Config) ->
+ ?SET_BOOL_XOPT(moderated, Val);
+set_xoption([{"members_by_default", [Val]} | Opts], Config) ->
+ ?SET_BOOL_XOPT(members_by_default, Val);
+set_xoption([{"members_only", [Val]} | Opts], Config) ->
+ ?SET_BOOL_XOPT(members_only, Val);
+set_xoption([{"allow_user_invites", [Val]} | Opts], Config) ->
+ ?SET_BOOL_XOPT(allow_user_invites, Val);
+set_xoption([{"password_protected", [Val]} | Opts], Config) ->
+ ?SET_BOOL_XOPT(password_protected, Val);
+set_xoption([{"anonymous", [Val]} | Opts], Config) ->
+ ?SET_BOOL_XOPT(anonymous, Val);
+set_xoption([{"logging", [Val]} | Opts], Config) ->
+ ?SET_BOOL_XOPT(logging, Val);
+set_xoption([_ | Opts], Config) ->
+ {error, ?ERR_BAD_REQUEST}.
+
+
+change_config(Config, StateData) ->
+ NSD = StateData#state{config = Config},
+ case {(StateData#state.config)#config.persistent,
+ Config#config.persistent} of
+ {_, true} ->
+ mod_muc:store_room(NSD#state.room, make_opts(NSD));
+ {true, false} ->
+ mod_muc:forget_room(NSD#state.room);
+ {false, false} ->
+ ok
+ end,
+ {result, [], NSD}.
+
+
+-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
+ ?CASE_CONFIG_OPT(allow_change_subj);
+ ?CASE_CONFIG_OPT(allow_query_users);
+ ?CASE_CONFIG_OPT(allow_private_messages);
+ ?CASE_CONFIG_OPT(public);
+ ?CASE_CONFIG_OPT(persistent);
+ ?CASE_CONFIG_OPT(moderated);
+ ?CASE_CONFIG_OPT(members_by_default);
+ ?CASE_CONFIG_OPT(members_only);
+ ?CASE_CONFIG_OPT(allow_user_invites);
+ ?CASE_CONFIG_OPT(password_protected);
+ ?CASE_CONFIG_OPT(anonymous);
+ ?CASE_CONFIG_OPT(logging);
+ affiliations ->
+ StateData#state{affiliations = ?DICT:from_list(Val)};
+ _ -> StateData
+ end,
+ set_opts(Opts, NSD).
+
+-define(MAKE_CONFIG_OPT(Opt), {Opt, Config#config.Opt}).
+
+make_opts(StateData) ->
+ Config = StateData#state.config,
+ [
+ ?MAKE_CONFIG_OPT(allow_change_subj),
+ ?MAKE_CONFIG_OPT(allow_query_users),
+ ?MAKE_CONFIG_OPT(allow_private_messages),
+ ?MAKE_CONFIG_OPT(public),
+ ?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),
+ ?MAKE_CONFIG_OPT(anonymous),
+ ?MAKE_CONFIG_OPT(logging),
+ {affiliations, ?DICT:to_list(StateData#state.affiliations)}
+ ].
+