mirror of
https://github.com/processone/ejabberd.git
synced 2024-10-05 14:51:05 +02:00
* (all): Enhanced virtual hosting support
SVN Revision: 370
This commit is contained in:
parent
cb90075327
commit
4098c3ecba
@ -1,3 +1,7 @@
|
||||
2005-06-20 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* (all): Enhanced virtual hosting support
|
||||
|
||||
2005-05-28 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/web/ejabberd_web_admin.erl: Bugfix
|
||||
|
95
src/acl.erl
95
src/acl.erl
@ -11,12 +11,12 @@
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/0,
|
||||
to_record/2,
|
||||
add/2,
|
||||
add_list/2,
|
||||
match_rule/2,
|
||||
to_record/3,
|
||||
add/3,
|
||||
add_list/3,
|
||||
match_rule/3,
|
||||
% for debugging only
|
||||
match_acl/2]).
|
||||
match_acl/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
@ -30,30 +30,35 @@ start() ->
|
||||
mnesia:add_table_copy(acl, node(), ram_copies),
|
||||
ok.
|
||||
|
||||
to_record(ACLName, ACLSpec) ->
|
||||
#acl{aclname = ACLName, aclspec = ACLSpec}.
|
||||
to_record(Host, ACLName, ACLSpec) ->
|
||||
#acl{aclname = {ACLName, Host}, aclspec = ACLSpec}.
|
||||
|
||||
add(ACLName, ACLSpec) ->
|
||||
add(Host, ACLName, ACLSpec) ->
|
||||
F = fun() ->
|
||||
mnesia:write(#acl{aclname = ACLName, aclspec = ACLSpec})
|
||||
mnesia:write(#acl{aclname = {ACLName, Host},
|
||||
aclspec = ACLSpec})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
add_list(ACLs, Clear) ->
|
||||
add_list(Host, ACLs, Clear) ->
|
||||
F = fun() ->
|
||||
if
|
||||
Clear ->
|
||||
Ks = mnesia:all_keys(acl),
|
||||
Ks = mnesia:select(
|
||||
acl, [{{acl, {'$1', Host}, '$2'}, [], ['$1']}]),
|
||||
lists:foreach(fun(K) ->
|
||||
mnesia:delete({acl, K})
|
||||
mnesia:delete({acl, {K, Host}})
|
||||
end, Ks);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
lists:foreach(fun(ACL) ->
|
||||
case ACL of
|
||||
#acl{} ->
|
||||
mnesia:write(ACL)
|
||||
#acl{aclname = ACLName,
|
||||
aclspec = ACLSpec} ->
|
||||
mnesia:write(
|
||||
#acl{aclname = {ACLName, Host},
|
||||
aclspec = ACLSpec})
|
||||
end
|
||||
end, ACLs)
|
||||
end,
|
||||
@ -66,30 +71,53 @@ add_list(ACLs, Clear) ->
|
||||
|
||||
|
||||
|
||||
match_rule(Rule, JID) ->
|
||||
match_rule(global, Rule, JID) ->
|
||||
case Rule of
|
||||
all -> allow;
|
||||
none -> deny;
|
||||
_ ->
|
||||
case ejabberd_config:get_global_option({access, Rule}) of
|
||||
case ejabberd_config:get_global_option({access, Rule, global}) of
|
||||
undefined ->
|
||||
deny;
|
||||
ACLs ->
|
||||
match_acls(ACLs, JID)
|
||||
GACLs ->
|
||||
match_acls(GACLs, JID, global)
|
||||
end
|
||||
end;
|
||||
|
||||
match_rule(Host, Rule, JID) ->
|
||||
case Rule of
|
||||
all -> allow;
|
||||
none -> deny;
|
||||
_ ->
|
||||
case ejabberd_config:get_global_option({access, Rule, global}) of
|
||||
undefined ->
|
||||
case ejabberd_config:get_global_option({access, Rule, Host}) of
|
||||
undefined ->
|
||||
deny;
|
||||
ACLs ->
|
||||
match_acls(ACLs, JID, Host)
|
||||
end;
|
||||
GACLs ->
|
||||
case ejabberd_config:get_global_option({access, Rule, Host}) of
|
||||
undefined ->
|
||||
match_acls(GACLs, JID, Host);
|
||||
ACLs ->
|
||||
match_acls(GACLs ++ ACLs, JID, Host)
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
match_acls([], _) ->
|
||||
match_acls([], _, Host) ->
|
||||
deny;
|
||||
match_acls([{Access, ACL} | ACLs], JID) ->
|
||||
case match_acl(ACL, JID) of
|
||||
match_acls([{Access, ACL} | ACLs], JID, Host) ->
|
||||
case match_acl(ACL, JID, Host) of
|
||||
true ->
|
||||
Access;
|
||||
_ ->
|
||||
match_acls(ACLs, JID)
|
||||
match_acls(ACLs, JID, Host)
|
||||
end.
|
||||
|
||||
match_acl(ACL, JID) ->
|
||||
match_acl(ACL, JID, Host) ->
|
||||
case ACL of
|
||||
all -> true;
|
||||
none -> false;
|
||||
@ -100,14 +128,20 @@ match_acl(ACL, JID) ->
|
||||
all ->
|
||||
true;
|
||||
{user, U} ->
|
||||
(U == User) andalso (?MYNAME == Server);
|
||||
(U == User)
|
||||
andalso
|
||||
((Host == Server) orelse
|
||||
((Host == global) andalso
|
||||
lists:member(Server, ?MYHOSTS)));
|
||||
{user, U, S} ->
|
||||
(U == User) andalso (S == Server);
|
||||
{server, S} ->
|
||||
S == Server;
|
||||
{user_regexp, UR} ->
|
||||
(?MYNAME == Server) andalso
|
||||
is_regexp_match(User, UR);
|
||||
((Host == Server) orelse
|
||||
((Host == global) andalso
|
||||
lists:member(Server, ?MYHOSTS)))
|
||||
andalso is_regexp_match(User, UR);
|
||||
{user_regexp, UR, S} ->
|
||||
(S == Server) andalso
|
||||
is_regexp_match(User, UR);
|
||||
@ -117,7 +151,10 @@ match_acl(ACL, JID) ->
|
||||
is_regexp_match(Server, SR) andalso
|
||||
is_regexp_match(User, UR);
|
||||
{user_glob, UR} ->
|
||||
(?MYNAME == Server) andalso
|
||||
((Host == Server) orelse
|
||||
((Host == global) andalso
|
||||
lists:member(Server, ?MYHOSTS)))
|
||||
andalso
|
||||
is_glob_match(User, UR);
|
||||
{user_glob, UR, S} ->
|
||||
(S == Server) andalso
|
||||
@ -128,7 +165,9 @@ match_acl(ACL, JID) ->
|
||||
is_glob_match(Server, SR) andalso
|
||||
is_glob_match(User, UR)
|
||||
end
|
||||
end, ets:lookup(acl, ACL))
|
||||
end,
|
||||
ets:lookup(acl, {ACL, global}) ++
|
||||
ets:lookup(acl, {ACL, Host}))
|
||||
end.
|
||||
|
||||
is_regexp_match(String, RegExp) ->
|
||||
|
@ -87,14 +87,18 @@ db_init() ->
|
||||
mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity).
|
||||
|
||||
load_modules() ->
|
||||
case ejabberd_config:get_local_option(modules) of
|
||||
undefined ->
|
||||
ok;
|
||||
Modules ->
|
||||
lists:foreach(fun({Module, Args}) ->
|
||||
gen_mod:start_module(Module, Args)
|
||||
end, Modules)
|
||||
end.
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
case ejabberd_config:get_local_option(modules) of
|
||||
undefined ->
|
||||
ok;
|
||||
Modules ->
|
||||
lists:foreach(
|
||||
fun({Module, Args}) ->
|
||||
gen_mod:start_module(Host, Module, Args)
|
||||
end, Modules)
|
||||
end
|
||||
end, ?MYHOSTS).
|
||||
|
||||
|
||||
dump_ports() ->
|
||||
|
@ -165,7 +165,7 @@ remove_user(User, Server) ->
|
||||
mnesia:delete({passwd, US})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
ejabberd_hooks:run(remove_user, [User, Server]).
|
||||
ejabberd_hooks:run(remove_user, LServer, [User, Server]).
|
||||
|
||||
remove_user(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
@ -184,7 +184,7 @@ remove_user(User, Server, Password) ->
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, ok} ->
|
||||
ejabberd_hooks:run(remove_user, [User, Server]),
|
||||
ejabberd_hooks:run(remove_user, LServer, [User, Server]),
|
||||
ok;
|
||||
{atomic, Res} ->
|
||||
Res;
|
||||
|
@ -170,7 +170,7 @@ is_user_exists(User, _Server) ->
|
||||
end
|
||||
end.
|
||||
|
||||
remove_user(User, _Server) ->
|
||||
remove_user(User, Server) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
error;
|
||||
@ -178,10 +178,10 @@ remove_user(User, _Server) ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
catch ejabberd_odbc:sql_query(
|
||||
["delete from users where username='", Username ,"'"]),
|
||||
ejabberd_hooks:run(remove_user, [User])
|
||||
ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User])
|
||||
end.
|
||||
|
||||
remove_user(User, _Server, Password) ->
|
||||
remove_user(User, Server, Password) ->
|
||||
case jlib:nodeprep(User) of
|
||||
error ->
|
||||
error;
|
||||
@ -196,7 +196,8 @@ remove_user(User, _Server, Password) ->
|
||||
"where username='", Username, "' and password='", Pass, "';"
|
||||
"commit"]) of
|
||||
{selected, ["password"], [{Password}]} ->
|
||||
ejabberd_hooks:run(remove_user, [User]),
|
||||
ejabberd_hooks:run(remove_user, jlib:nameprep(Server),
|
||||
[User]),
|
||||
ok;
|
||||
{selected, ["password"], []} ->
|
||||
not_exists;
|
||||
|
@ -340,7 +340,8 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
{auth, _ID, set, {U, P, D, R}} ->
|
||||
JID = jlib:make_jid(U, StateData#state.server, R),
|
||||
case (JID /= error) andalso
|
||||
(acl:match_rule(StateData#state.access, JID) == allow) of
|
||||
(acl:match_rule(StateData#state.server,
|
||||
StateData#state.access, JID) == allow) of
|
||||
true ->
|
||||
case ejabberd_auth:check_password(
|
||||
U, StateData#state.server, P,
|
||||
@ -358,6 +359,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
change_shaper(StateData, JID),
|
||||
{Fs, Ts} = ejabberd_hooks:run_fold(
|
||||
roster_get_subscription_lists,
|
||||
StateData#state.server,
|
||||
{[], []},
|
||||
[U, StateData#state.server]),
|
||||
LJID = jlib:jid_tolower(
|
||||
@ -651,7 +653,8 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
|
||||
U = StateData#state.user,
|
||||
R = StateData#state.resource,
|
||||
JID = StateData#state.jid,
|
||||
case acl:match_rule(StateData#state.access, JID) of
|
||||
case acl:match_rule(StateData#state.server,
|
||||
StateData#state.access, JID) of
|
||||
allow ->
|
||||
?INFO_MSG("(~w) Opened session for ~s",
|
||||
[StateData#state.socket,
|
||||
@ -663,6 +666,7 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
|
||||
change_shaper(StateData, JID),
|
||||
{Fs, Ts} = ejabberd_hooks:run_fold(
|
||||
roster_get_subscription_lists,
|
||||
StateData#state.server,
|
||||
{[], []},
|
||||
[U, StateData#state.server]),
|
||||
LJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
@ -764,6 +768,7 @@ session_established({xmlstreamelement, El}, StateData) ->
|
||||
_ ->
|
||||
ejabberd_hooks:run(
|
||||
user_send_packet,
|
||||
Server,
|
||||
[FromJID, ToJID, NewEl]),
|
||||
ejabberd_router:route(
|
||||
FromJID, ToJID, NewEl),
|
||||
@ -772,6 +777,7 @@ session_established({xmlstreamelement, El}, StateData) ->
|
||||
end;
|
||||
"message" ->
|
||||
ejabberd_hooks:run(user_send_packet,
|
||||
Server,
|
||||
[FromJID, ToJID, NewEl]),
|
||||
ejabberd_router:route(FromJID, ToJID, NewEl),
|
||||
StateData;
|
||||
@ -983,6 +989,7 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
Text = xml:element_to_string(FixedPacket),
|
||||
send_text(StateData, Text),
|
||||
ejabberd_hooks:run(user_receive_packet,
|
||||
StateData#state.server,
|
||||
[StateData#state.jid, From, To, FixedPacket]),
|
||||
{next_state, StateName, NewState};
|
||||
true ->
|
||||
@ -1055,7 +1062,8 @@ terminate(_Reason, StateName, StateData) ->
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
change_shaper(StateData, JID) ->
|
||||
Shaper = acl:match_rule(StateData#state.shaper, JID),
|
||||
Shaper = acl:match_rule(StateData#state.server,
|
||||
StateData#state.shaper, JID),
|
||||
ejabberd_receiver:change_shaper(StateData#state.receiver, Shaper).
|
||||
|
||||
send_text(StateData, Text) ->
|
||||
@ -1208,6 +1216,7 @@ presence_update(From, Packet, StateData) ->
|
||||
if
|
||||
FromUnavail ->
|
||||
ejabberd_hooks:run(user_available_hook,
|
||||
StateData#state.server,
|
||||
[StateData#state.jid]),
|
||||
resend_offline_messages(StateData),
|
||||
presence_broadcast_first(
|
||||
@ -1248,21 +1257,25 @@ presence_track(From, To, Packet, StateData) ->
|
||||
"subscribe" ->
|
||||
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
Server,
|
||||
[User, Server, To, subscribe]),
|
||||
StateData;
|
||||
"subscribed" ->
|
||||
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
Server,
|
||||
[User, Server, To, subscribed]),
|
||||
StateData;
|
||||
"unsubscribe" ->
|
||||
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
Server,
|
||||
[User, Server, To, unsubscribe]),
|
||||
StateData;
|
||||
"unsubscribed" ->
|
||||
ejabberd_router:route(jlib:jid_remove_resource(From), To, Packet),
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
Server,
|
||||
[User, Server, To, unsubscribed]),
|
||||
StateData;
|
||||
"error" ->
|
||||
@ -1505,7 +1518,9 @@ process_privacy_iq(From, To,
|
||||
resend_offline_messages(#state{user = User,
|
||||
server = Server,
|
||||
privacy_list = PrivList} = StateData) ->
|
||||
case ejabberd_hooks:run_fold(resend_offline_messages_hook, [],
|
||||
case ejabberd_hooks:run_fold(resend_offline_messages_hook,
|
||||
Server,
|
||||
[],
|
||||
[User, Server]) of
|
||||
Rs when list(Rs) ->
|
||||
lists:foreach(
|
||||
|
@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_config.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose :
|
||||
%%% Purpose : Load config file
|
||||
%%% Created : 14 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
@ -19,12 +19,12 @@
|
||||
-record(config, {key, value}).
|
||||
-record(local_config, {key, value}).
|
||||
-record(state, {opts = [],
|
||||
hosts = [],
|
||||
override_local = false,
|
||||
override_global = false,
|
||||
override_acls = false}).
|
||||
|
||||
start() ->
|
||||
%ets:new(ejabberd_config, [named_table, public]),
|
||||
mnesia:create_table(config,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, config)}]),
|
||||
@ -34,8 +34,6 @@ start() ->
|
||||
{local_content, true},
|
||||
{attributes, record_info(fields, local_config)}]),
|
||||
mnesia:add_table_copy(local_config, node(), ram_copies),
|
||||
|
||||
%% mremond: Config file can be configured from the command line
|
||||
Config = case application:get_env(config) of
|
||||
{ok, Path} -> Path;
|
||||
undefined ->
|
||||
@ -52,13 +50,38 @@ start() ->
|
||||
load_file(File) ->
|
||||
case file:consult(File) of
|
||||
{ok, Terms} ->
|
||||
Res = lists:foldl(fun process_term/2, #state{}, Terms),
|
||||
State = lists:foldl(fun search_hosts/2, #state{}, Terms),
|
||||
Res = lists:foldl(fun process_term/2, State, Terms),
|
||||
set_opts(Res);
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Can't load config file ~p: ~p", [File, Reason]),
|
||||
exit(file:format_error(Reason))
|
||||
end.
|
||||
|
||||
search_hosts(Term, State) ->
|
||||
case Term of
|
||||
{host, Host} ->
|
||||
if
|
||||
State#state.hosts == [] ->
|
||||
add_option(hosts, [Host], State#state{hosts = [Host]});
|
||||
true ->
|
||||
?ERROR_MSG("Can't load config file: "
|
||||
"too many hosts definitions", []),
|
||||
exit("too many hosts definitions")
|
||||
end;
|
||||
{hosts, Hosts} ->
|
||||
if
|
||||
State#state.hosts == [] ->
|
||||
add_option(hosts, Hosts, State#state{hosts = Hosts});
|
||||
true ->
|
||||
?ERROR_MSG("Can't load config file: "
|
||||
"too many hosts definitions", []),
|
||||
exit("too many hosts definitions")
|
||||
end;
|
||||
_ ->
|
||||
State
|
||||
end.
|
||||
|
||||
process_term(Term, State) ->
|
||||
case Term of
|
||||
override_global ->
|
||||
@ -68,20 +91,40 @@ process_term(Term, State) ->
|
||||
override_acls ->
|
||||
State#state{override_acls = true};
|
||||
{acl, ACLName, ACLData} ->
|
||||
State#state{opts =
|
||||
[acl:to_record(ACLName, ACLData) | State#state.opts]};
|
||||
process_host_term(Term, global, State);
|
||||
{access, RuleName, Rules} ->
|
||||
State#state{opts = [#config{key = {access, RuleName},
|
||||
process_host_term(Term, global, State);
|
||||
{shaper, Name, Data} ->
|
||||
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
|
||||
State, State#state.hosts);
|
||||
{host, Host} ->
|
||||
State;
|
||||
{hosts, Hosts} ->
|
||||
State;
|
||||
{Opt, Val} ->
|
||||
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
|
||||
State, State#state.hosts)
|
||||
end.
|
||||
|
||||
process_host_term(Term, Host, State) ->
|
||||
case Term of
|
||||
{acl, ACLName, ACLData} ->
|
||||
State#state{opts =
|
||||
[acl:to_record(Host, ACLName, ACLData) | State#state.opts]};
|
||||
{access, RuleName, Rules} ->
|
||||
State#state{opts = [#config{key = {access, RuleName, Host},
|
||||
value = Rules} |
|
||||
State#state.opts]};
|
||||
{shaper, Name, Data} ->
|
||||
State#state{opts = [#config{key = {shaper, Name},
|
||||
State#state{opts = [#config{key = {shaper, Name, Host},
|
||||
value = Data} |
|
||||
State#state.opts]};
|
||||
{host, Host} ->
|
||||
add_option(hosts, [Host], State);
|
||||
State;
|
||||
{hosts, Hosts} ->
|
||||
State;
|
||||
{Opt, Val} ->
|
||||
add_option(Opt, Val, State)
|
||||
add_option({Opt, Host}, Val, State)
|
||||
end.
|
||||
|
||||
add_option(Opt, Val, State) ->
|
||||
|
@ -16,7 +16,11 @@
|
||||
add/4,
|
||||
delete/4,
|
||||
run/2,
|
||||
run_fold/3]).
|
||||
run_fold/3,
|
||||
add/5,
|
||||
delete/5,
|
||||
run/3,
|
||||
run_fold/4]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
@ -37,13 +41,22 @@ start_link() ->
|
||||
gen_server:start_link({local, ejabberd_hooks}, ejabberd_hooks, [], []).
|
||||
|
||||
add(Hook, Module, Function, Seq) ->
|
||||
gen_server:call(ejabberd_hooks, {add, Hook, Module, Function, Seq}).
|
||||
add(Hook, global, Module, Function, Seq).
|
||||
|
||||
add(Hook, Host, Module, Function, Seq) ->
|
||||
gen_server:call(ejabberd_hooks, {add, Hook, Host, Module, Function, Seq}).
|
||||
|
||||
delete(Hook, Module, Function, Seq) ->
|
||||
gen_server:call(ejabberd_hooks, {delete, Hook, Module, Function, Seq}).
|
||||
delete(Hook, global, Module, Function, Seq).
|
||||
|
||||
delete(Hook, Host, Module, Function, Seq) ->
|
||||
gen_server:call(ejabberd_hooks, {delete, Hook, Host, Module, Function, Seq}).
|
||||
|
||||
run(Hook, Args) ->
|
||||
case ets:lookup(hooks, Hook) of
|
||||
run(Hook, global, Args).
|
||||
|
||||
run(Hook, Host, Args) ->
|
||||
case ets:lookup(hooks, {Hook, Host}) of
|
||||
[{_, Ls}] ->
|
||||
run1(Ls, Hook, Args);
|
||||
[] ->
|
||||
@ -51,7 +64,10 @@ run(Hook, Args) ->
|
||||
end.
|
||||
|
||||
run_fold(Hook, Val, Args) ->
|
||||
case ets:lookup(hooks, Hook) of
|
||||
run_fold(Hook, global, Val, Args).
|
||||
|
||||
run_fold(Hook, Host, Val, Args) ->
|
||||
case ets:lookup(hooks, {Hook, Host}) of
|
||||
[{_, Ls}] ->
|
||||
run_fold1(Ls, Hook, Val, Args);
|
||||
[] ->
|
||||
@ -82,8 +98,8 @@ init([]) ->
|
||||
%% {stop, Reason, Reply, State} | (terminate/2 is called)
|
||||
%% {stop, Reason, State} (terminate/2 is called)
|
||||
%%----------------------------------------------------------------------
|
||||
handle_call({add, Hook, Module, Function, Seq}, From, State) ->
|
||||
Reply = case ets:lookup(hooks, Hook) of
|
||||
handle_call({add, Hook, Host, Module, Function, Seq}, From, State) ->
|
||||
Reply = case ets:lookup(hooks, {Hook, Host}) of
|
||||
[{_, Ls}] ->
|
||||
El = {Seq, Module, Function},
|
||||
case lists:member(El, Ls) of
|
||||
@ -91,20 +107,20 @@ handle_call({add, Hook, Module, Function, Seq}, From, State) ->
|
||||
ok;
|
||||
false ->
|
||||
NewLs = lists:merge(Ls, [El]),
|
||||
ets:insert(hooks, {Hook, NewLs}),
|
||||
ets:insert(hooks, {{Hook, Host}, NewLs}),
|
||||
ok
|
||||
end;
|
||||
[] ->
|
||||
NewLs = [{Seq, Module, Function}],
|
||||
ets:insert(hooks, {Hook, NewLs}),
|
||||
ets:insert(hooks, {{Hook, Host}, NewLs}),
|
||||
ok
|
||||
end,
|
||||
{reply, Reply, State};
|
||||
handle_call({delete, Hook, Module, Function, Seq}, From, State) ->
|
||||
Reply = case ets:lookup(hooks, Hook) of
|
||||
handle_call({delete, Hook, Host, Module, Function, Seq}, From, State) ->
|
||||
Reply = case ets:lookup(hooks, {Hook, Host}) of
|
||||
[{_, Ls}] ->
|
||||
NewLs = lists:delete({Seq, Module, Function}, Ls),
|
||||
ets:insert(hooks, {Hook, NewLs}),
|
||||
ets:insert(hooks, {{Hook, Host}, NewLs}),
|
||||
ok;
|
||||
[] ->
|
||||
ok
|
||||
|
@ -13,9 +13,9 @@
|
||||
-export([start_link/0, init/0]).
|
||||
|
||||
-export([route/3,
|
||||
register_iq_handler/3,
|
||||
register_iq_handler/4,
|
||||
unregister_iq_handler/1,
|
||||
register_iq_handler/5,
|
||||
unregister_iq_handler/2,
|
||||
refresh_iq_handlers/0,
|
||||
bounce_resource_packet/3
|
||||
]).
|
||||
@ -33,11 +33,11 @@ start_link() ->
|
||||
init() ->
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ejabberd_router:register_route(Host, {apply, ?MODULE, route})
|
||||
ejabberd_router:register_route(Host, {apply, ?MODULE, route}),
|
||||
ejabberd_hooks:add(local_send_to_resource_hook, Host,
|
||||
?MODULE, bounce_resource_packet, 100)
|
||||
end, ?MYHOSTS),
|
||||
catch ets:new(?IQTABLE, [named_table, public]),
|
||||
ejabberd_hooks:add(local_send_to_resource_hook,
|
||||
?MODULE, bounce_resource_packet, 100),
|
||||
loop().
|
||||
|
||||
loop() ->
|
||||
@ -51,32 +51,32 @@ loop() ->
|
||||
ok
|
||||
end,
|
||||
loop();
|
||||
{register_iq_handler, XMLNS, Module, Function} ->
|
||||
ets:insert(?IQTABLE, {XMLNS, Module, Function}),
|
||||
catch mod_disco:register_feature(XMLNS),
|
||||
{register_iq_handler, Host, XMLNS, Module, Function} ->
|
||||
ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function}),
|
||||
catch mod_disco:register_feature(Host, XMLNS),
|
||||
loop();
|
||||
{register_iq_handler, XMLNS, Module, Function, Opts} ->
|
||||
ets:insert(?IQTABLE, {XMLNS, Module, Function, Opts}),
|
||||
catch mod_disco:register_feature(XMLNS),
|
||||
{register_iq_handler, Host, XMLNS, Module, Function, Opts} ->
|
||||
ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function, Opts}),
|
||||
catch mod_disco:register_feature(Host, XMLNS),
|
||||
loop();
|
||||
{unregister_iq_handler, XMLNS} ->
|
||||
case ets:lookup(?IQTABLE, XMLNS) of
|
||||
{unregister_iq_handler, Host, XMLNS} ->
|
||||
case ets:lookup(?IQTABLE, {XMLNS, Host}) of
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
ets:delete(?IQTABLE, XMLNS),
|
||||
catch mod_disco:unregister_feature(XMLNS),
|
||||
ets:delete(?IQTABLE, {XMLNS, Host}),
|
||||
catch mod_disco:unregister_feature(Host, XMLNS),
|
||||
loop();
|
||||
refresh_iq_handlers ->
|
||||
lists:foreach(
|
||||
fun(T) ->
|
||||
case T of
|
||||
{XMLNS, _Module, _Function, _Opts} ->
|
||||
catch mod_disco:register_feature(XMLNS);
|
||||
{XMLNS, _Module, _Function} ->
|
||||
catch mod_disco:register_feature(XMLNS);
|
||||
{{XMLNS, Host}, _Module, _Function, _Opts} ->
|
||||
catch mod_disco:register_feature(Host, XMLNS);
|
||||
{{XMLNS, Host}, _Module, _Function} ->
|
||||
catch mod_disco:register_feature(Host, XMLNS);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
@ -112,6 +112,7 @@ do_route(From, To, Packet) ->
|
||||
"result" -> ok;
|
||||
_ ->
|
||||
ejabberd_hooks:run(local_send_to_resource_hook,
|
||||
To#jid.lserver,
|
||||
[From, To, Packet])
|
||||
end
|
||||
end.
|
||||
@ -120,7 +121,8 @@ process_iq(From, To, Packet) ->
|
||||
IQ = jlib:iq_query_info(Packet),
|
||||
case IQ of
|
||||
#iq{xmlns = XMLNS} ->
|
||||
case ets:lookup(?IQTABLE, XMLNS) of
|
||||
Host = To#jid.lserver,
|
||||
case ets:lookup(?IQTABLE, {XMLNS, Host}) of
|
||||
[{_, Module, Function}] ->
|
||||
ResIQ = Module:Function(From, To, IQ),
|
||||
if
|
||||
@ -131,7 +133,7 @@ process_iq(From, To, Packet) ->
|
||||
ok
|
||||
end;
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:handle(Module, Function, Opts,
|
||||
gen_iq_handler:handle(Host, Module, Function, Opts,
|
||||
From, To, IQ);
|
||||
[] ->
|
||||
Err = jlib:make_error_reply(
|
||||
@ -155,14 +157,14 @@ route(From, To, Packet) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
register_iq_handler(XMLNS, Module, Fun) ->
|
||||
ejabberd_local ! {register_iq_handler, XMLNS, Module, Fun}.
|
||||
register_iq_handler(Host, XMLNS, Module, Fun) ->
|
||||
ejabberd_local ! {register_iq_handler, Host, XMLNS, Module, Fun}.
|
||||
|
||||
register_iq_handler(XMLNS, Module, Fun, Opts) ->
|
||||
ejabberd_local ! {register_iq_handler, XMLNS, Module, Fun, Opts}.
|
||||
register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->
|
||||
ejabberd_local ! {register_iq_handler, Host, XMLNS, Module, Fun, Opts}.
|
||||
|
||||
unregister_iq_handler(XMLNS) ->
|
||||
ejabberd_local ! {unregister_iq_handler, XMLNS}.
|
||||
unregister_iq_handler(Host, XMLNS) ->
|
||||
ejabberd_local ! {unregister_iq_handler, Host, XMLNS}.
|
||||
|
||||
refresh_iq_handlers() ->
|
||||
ejabberd_local ! refresh_iq_handlers.
|
||||
|
@ -148,7 +148,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
|
||||
Key, StateData#state.streamid}),
|
||||
Conns = ?DICT:store({LFrom, LTo}, wait_for_verification,
|
||||
StateData#state.connections),
|
||||
change_shaper(StateData, jlib:make_jid("", LFrom, "")),
|
||||
change_shaper(StateData, LTo, jlib:make_jid("", LFrom, "")),
|
||||
{next_state,
|
||||
stream_established,
|
||||
StateData#state{connections = Conns,
|
||||
@ -326,8 +326,8 @@ send_element(Socket, El) ->
|
||||
send_text(Socket, xml:element_to_string(El)).
|
||||
|
||||
|
||||
change_shaper(StateData, JID) ->
|
||||
Shaper = acl:match_rule(StateData#state.shaper, JID),
|
||||
change_shaper(StateData, Host, JID) ->
|
||||
Shaper = acl:match_rule(Host, StateData#state.shaper, JID),
|
||||
ejabberd_receiver:change_shaper(StateData#state.receiver, Shaper).
|
||||
|
||||
|
||||
|
@ -283,7 +283,7 @@ handle_info({send_element, El}, StateName, StateData) ->
|
||||
send_element(StateData, El),
|
||||
{next_state, StateName, StateData};
|
||||
handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
case acl:match_rule(StateData#state.access, From) of
|
||||
case acl:match_rule(global, StateData#state.access, From) of
|
||||
allow ->
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
|
||||
|
@ -21,9 +21,9 @@
|
||||
dirty_get_sessions_list/0,
|
||||
dirty_get_my_sessions_list/0,
|
||||
get_vh_session_list/1,
|
||||
register_iq_handler/3,
|
||||
register_iq_handler/4,
|
||||
unregister_iq_handler/1
|
||||
register_iq_handler/5,
|
||||
unregister_iq_handler/2
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@ -49,10 +49,13 @@ init() ->
|
||||
mnesia:add_table_index(presence, us),
|
||||
mnesia:subscribe(system),
|
||||
ets:new(sm_iqtable, [named_table]),
|
||||
ejabberd_hooks:add(offline_message_hook,
|
||||
ejabberd_sm, bounce_offline_message, 100),
|
||||
ejabberd_hooks:add(remove_user,
|
||||
ejabberd_sm, disconnect_removed_user, 100),
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ejabberd_hooks:add(offline_message_hook, Host,
|
||||
ejabberd_sm, bounce_offline_message, 100),
|
||||
ejabberd_hooks:add(remove_user, Host,
|
||||
ejabberd_sm, disconnect_removed_user, 100)
|
||||
end, ?MYHOSTS),
|
||||
loop().
|
||||
|
||||
loop() ->
|
||||
@ -69,20 +72,20 @@ loop() ->
|
||||
{mnesia_system_event, {mnesia_down, Node}} ->
|
||||
clean_table_from_bad_node(Node),
|
||||
loop();
|
||||
{register_iq_handler, XMLNS, Module, Function} ->
|
||||
ets:insert(sm_iqtable, {XMLNS, Module, Function}),
|
||||
{register_iq_handler, Host, XMLNS, Module, Function} ->
|
||||
ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function}),
|
||||
loop();
|
||||
{register_iq_handler, XMLNS, Module, Function, Opts} ->
|
||||
ets:insert(sm_iqtable, {XMLNS, Module, Function, Opts}),
|
||||
{register_iq_handler, Host, XMLNS, Module, Function, Opts} ->
|
||||
ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function, Opts}),
|
||||
loop();
|
||||
{unregister_iq_handler, XMLNS} ->
|
||||
case ets:lookup(sm_iqtable, XMLNS) of
|
||||
{unregister_iq_handler, Host, XMLNS} ->
|
||||
case ets:lookup(sm_iqtable, {XMLNS, Host}) of
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
ets:delete(sm_iqtable, XMLNS),
|
||||
ets:delete(sm_iqtable, {XMLNS, Host}),
|
||||
loop();
|
||||
_ ->
|
||||
loop()
|
||||
@ -170,24 +173,28 @@ do_route(From, To, Packet) ->
|
||||
"subscribe" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
LServer,
|
||||
false,
|
||||
[User, Server, From, subscribe]),
|
||||
true};
|
||||
"subscribed" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
LServer,
|
||||
false,
|
||||
[User, Server, From, subscribed]),
|
||||
true};
|
||||
"unsubscribe" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
LServer,
|
||||
false,
|
||||
[User, Server, From, unsubscribe]),
|
||||
true};
|
||||
"unsubscribed" ->
|
||||
{ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
LServer,
|
||||
false,
|
||||
[User, Server, From, unsubscribed]),
|
||||
true};
|
||||
@ -220,6 +227,7 @@ do_route(From, To, Packet) ->
|
||||
true ->
|
||||
ejabberd_hooks:run(
|
||||
offline_subscription_hook,
|
||||
LServer,
|
||||
[From, To, Packet]);
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
@ -298,6 +306,7 @@ route_message(From, To, Packet) ->
|
||||
case ejabberd_auth:is_user_exists(LUser, LServer) of
|
||||
true ->
|
||||
ejabberd_hooks:run(offline_message_hook,
|
||||
LServer,
|
||||
[From, To, Packet]);
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
@ -354,7 +363,8 @@ unset_presence(User, Server, Resource, Status) ->
|
||||
mnesia:delete({presence, USR})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
ejabberd_hooks:run(unset_presence_hook, [User, Server, Resource, Status]).
|
||||
ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server),
|
||||
[User, Server, Resource, Status]).
|
||||
|
||||
get_user_present_resources(LUser, LServer) ->
|
||||
US = {LUser, LServer},
|
||||
@ -392,7 +402,8 @@ process_iq(From, To, Packet) ->
|
||||
IQ = jlib:iq_query_info(Packet),
|
||||
case IQ of
|
||||
#iq{xmlns = XMLNS} ->
|
||||
case ets:lookup(sm_iqtable, XMLNS) of
|
||||
Host = To#jid.lserver,
|
||||
case ets:lookup(sm_iqtable, {XMLNS, Host}) of
|
||||
[{_, Module, Function}] ->
|
||||
ResIQ = Module:Function(From, To, IQ),
|
||||
if
|
||||
@ -403,7 +414,7 @@ process_iq(From, To, Packet) ->
|
||||
ok
|
||||
end;
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:handle(Module, Function, Opts,
|
||||
gen_iq_handler:handle(Host, Module, Function, Opts,
|
||||
From, To, IQ);
|
||||
[] ->
|
||||
Err = jlib:make_error_reply(
|
||||
@ -418,14 +429,14 @@ process_iq(From, To, Packet) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
register_iq_handler(XMLNS, Module, Fun) ->
|
||||
ejabberd_sm ! {register_iq_handler, XMLNS, Module, Fun}.
|
||||
register_iq_handler(Host, XMLNS, Module, Fun) ->
|
||||
ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun}.
|
||||
|
||||
register_iq_handler(XMLNS, Module, Fun, Opts) ->
|
||||
ejabberd_sm ! {register_iq_handler, XMLNS, Module, Fun, Opts}.
|
||||
register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->
|
||||
ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun, Opts}.
|
||||
|
||||
unregister_iq_handler(XMLNS) ->
|
||||
ejabberd_sm ! {unregister_iq_handler, XMLNS}.
|
||||
unregister_iq_handler(Host, XMLNS) ->
|
||||
ejabberd_sm ! {unregister_iq_handler, Host, XMLNS}.
|
||||
|
||||
|
||||
|
||||
|
@ -11,33 +11,33 @@
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/0,
|
||||
start_link/2,
|
||||
add_iq_handler/5,
|
||||
remove_iq_handler/2,
|
||||
start_link/3,
|
||||
add_iq_handler/6,
|
||||
remove_iq_handler/3,
|
||||
stop_iq_handler/3,
|
||||
handle/6,
|
||||
process_iq/5,
|
||||
queue_init/2]).
|
||||
handle/7,
|
||||
process_iq/6,
|
||||
queue_init/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
start() ->
|
||||
ok.
|
||||
|
||||
add_iq_handler(Component, NS, Module, Function, Type) ->
|
||||
add_iq_handler(Component, Host, NS, Module, Function, Type) ->
|
||||
case Type of
|
||||
no_queue ->
|
||||
Component:register_iq_handler(NS, Module, Function, no_queue);
|
||||
Component:register_iq_handler(Host, NS, Module, Function, no_queue);
|
||||
one_queue ->
|
||||
{ok, Pid} = supervisor:start_child(ejabberd_iq_sup,
|
||||
[Module, Function]),
|
||||
Component:register_iq_handler(NS, Module, Function,
|
||||
[Host, Module, Function]),
|
||||
Component:register_iq_handler(Host, NS, Module, Function,
|
||||
{one_queue, Pid});
|
||||
parallel ->
|
||||
Component:register_iq_handler(NS, Module, Function, parallel)
|
||||
Component:register_iq_handler(Host, NS, Module, Function, parallel)
|
||||
end.
|
||||
|
||||
remove_iq_handler(Component, NS) ->
|
||||
remove_iq_handler(Component, Host, NS) ->
|
||||
Component:unregister_iq_handler(NS).
|
||||
|
||||
stop_iq_handler(Module, Function, Opts) ->
|
||||
@ -48,20 +48,20 @@ stop_iq_handler(Module, Function, Opts) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
handle(Module, Function, Opts, From, To, IQ) ->
|
||||
handle(Host, Module, Function, Opts, From, To, IQ) ->
|
||||
case Opts of
|
||||
no_queue ->
|
||||
process_iq(Module, Function, From, To, IQ);
|
||||
process_iq(Host, Module, Function, From, To, IQ);
|
||||
{one_queue, Pid} ->
|
||||
Pid ! {process_iq, From, To, IQ};
|
||||
parallel ->
|
||||
spawn(?MODULE, process_iq, [Module, Function, From, To, IQ]);
|
||||
spawn(?MODULE, process_iq, [Host, Module, Function, From, To, IQ]);
|
||||
_ ->
|
||||
todo
|
||||
end.
|
||||
|
||||
|
||||
process_iq(Module, Function, From, To, IQ) ->
|
||||
process_iq(_Host, Module, Function, From, To, IQ) ->
|
||||
case catch Module:Function(From, To, IQ) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
@ -75,18 +75,18 @@ process_iq(Module, Function, From, To, IQ) ->
|
||||
end
|
||||
end.
|
||||
|
||||
start_link(Module, Function) ->
|
||||
{ok, proc_lib:spawn_link(?MODULE, queue_init, [Module, Function])}.
|
||||
start_link(Host, Module, Function) ->
|
||||
{ok, proc_lib:spawn_link(?MODULE, queue_init, [Host, Module, Function])}.
|
||||
|
||||
queue_init(Module, Function) ->
|
||||
queue_loop(Module, Function).
|
||||
queue_init(Host, Module, Function) ->
|
||||
queue_loop(Host, Module, Function).
|
||||
|
||||
% TODO: use gen_event
|
||||
queue_loop(Module, Function) ->
|
||||
queue_loop(Host, Module, Function) ->
|
||||
receive
|
||||
{process_iq, From, To, IQ} ->
|
||||
process_iq(Module, Function, From, To, IQ),
|
||||
queue_loop(Module, Function);
|
||||
process_iq(Host, Module, Function, From, To, IQ),
|
||||
queue_loop(Host, Module, Function);
|
||||
_ ->
|
||||
queue_loop(Module, Function)
|
||||
queue_loop(Host, Module, Function)
|
||||
end.
|
||||
|
@ -11,58 +11,60 @@
|
||||
-vsn('$Revision$ ').
|
||||
|
||||
-export([start/0,
|
||||
start_module/2,
|
||||
stop_module/1,
|
||||
start_module/3,
|
||||
stop_module/2,
|
||||
get_opt/2,
|
||||
get_opt/3,
|
||||
get_module_opt/3,
|
||||
loaded_modules/0,
|
||||
loaded_modules_with_opts/0,
|
||||
get_hosts/2]).
|
||||
get_module_opt/4,
|
||||
loaded_modules/1,
|
||||
loaded_modules_with_opts/1,
|
||||
get_hosts/2,
|
||||
get_module_proc/2]).
|
||||
|
||||
-export([behaviour_info/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(ejabberd_module, {module, opts}).
|
||||
-record(ejabberd_module, {module_host, opts}).
|
||||
|
||||
behaviour_info(callbacks) ->
|
||||
[{start, 1},
|
||||
{stop, 0}];
|
||||
[{start, 2},
|
||||
{stop, 1}];
|
||||
behaviour_info(_Other) ->
|
||||
undefined.
|
||||
|
||||
start() ->
|
||||
ets:new(ejabberd_modules, [named_table,
|
||||
public,
|
||||
{keypos, #ejabberd_module.module}]),
|
||||
{keypos, #ejabberd_module.module_host}]),
|
||||
ok.
|
||||
|
||||
|
||||
start_module(Module, Opts) ->
|
||||
case catch Module:start(Opts) of
|
||||
start_module(Host, Module, Opts) ->
|
||||
case catch Module:start(Host, Opts) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
_ ->
|
||||
ets:insert(ejabberd_modules, #ejabberd_module{module = Module,
|
||||
opts = Opts}),
|
||||
ets:insert(ejabberd_modules,
|
||||
#ejabberd_module{module_host = {Module, Host},
|
||||
opts = Opts}),
|
||||
ok
|
||||
end.
|
||||
|
||||
stop_module(Module) ->
|
||||
case catch Module:stop() of
|
||||
stop_module(Host, Module) ->
|
||||
case catch Module:stop(Host) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p", [Reason]);
|
||||
{wait, ProcList} when is_list(ProcList) ->
|
||||
lists:foreach(fun wait_for_process/1, ProcList),
|
||||
ets:delete(ejabberd_modules, Module),
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
ok;
|
||||
{wait, Process} ->
|
||||
wait_for_process(Process),
|
||||
ets:delete(ejabberd_modules, Module),
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
ok;
|
||||
_ ->
|
||||
ets:delete(ejabberd_modules, Module),
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
ok
|
||||
end.
|
||||
|
||||
@ -104,8 +106,8 @@ get_opt(Opt, Opts, Default) ->
|
||||
Val
|
||||
end.
|
||||
|
||||
get_module_opt(Module, Opt, Default) ->
|
||||
OptsList = ets:lookup(ejabberd_modules, Module),
|
||||
get_module_opt(Host, Module, Opt, Default) ->
|
||||
OptsList = ets:lookup(ejabberd_modules, {Module, Host}),
|
||||
case OptsList of
|
||||
[] ->
|
||||
Default;
|
||||
@ -113,15 +115,16 @@ get_module_opt(Module, Opt, Default) ->
|
||||
get_opt(Opt, Opts, Default)
|
||||
end.
|
||||
|
||||
loaded_modules() ->
|
||||
loaded_modules(Host) ->
|
||||
ets:select(ejabberd_modules,
|
||||
[{#ejabberd_module{_ = '_', module = '$1'},
|
||||
[{#ejabberd_module{_ = '_', module_host = {'$1', Host}},
|
||||
[],
|
||||
['$1']}]).
|
||||
|
||||
loaded_modules_with_opts() ->
|
||||
loaded_modules_with_opts(Host) ->
|
||||
ets:select(ejabberd_modules,
|
||||
[{#ejabberd_module{_ = '_', module = '$1', opts = '$2'},
|
||||
[{#ejabberd_module{_ = '_', module_host = {'$1', Host},
|
||||
opts = '$2'},
|
||||
[],
|
||||
[{{'$1', '$2'}}]}]).
|
||||
|
||||
@ -137,3 +140,7 @@ get_hosts(Opts, Prefix) ->
|
||||
Hosts ->
|
||||
Hosts
|
||||
end.
|
||||
|
||||
get_module_proc(Host, Base) ->
|
||||
list_to_atom(atom_to_list(Base) ++ "_" ++ Host).
|
||||
|
||||
|
@ -11,9 +11,9 @@
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/1,
|
||||
-export([start/2,
|
||||
init/0,
|
||||
stop/0,
|
||||
stop/1,
|
||||
announce/3,
|
||||
send_motd/1]).
|
||||
|
||||
@ -25,17 +25,18 @@
|
||||
|
||||
-define(PROCNAME, ejabberd_announce).
|
||||
|
||||
start(_) ->
|
||||
start(Host, _Opts) ->
|
||||
mnesia:create_table(motd, [{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, motd)}]),
|
||||
mnesia:create_table(motd_users, [{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, motd_users)}]),
|
||||
update_tables(),
|
||||
ejabberd_hooks:add(local_send_to_resource_hook,
|
||||
ejabberd_hooks:add(local_send_to_resource_hook, Host,
|
||||
?MODULE, announce, 50),
|
||||
ejabberd_hooks:add(user_available_hook,
|
||||
ejabberd_hooks:add(user_available_hook, Host,
|
||||
?MODULE, send_motd, 50),
|
||||
register(?PROCNAME, proc_lib:spawn(?MODULE, init, [])).
|
||||
register(gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
proc_lib:spawn(?MODULE, init, [])).
|
||||
|
||||
init() ->
|
||||
loop().
|
||||
@ -64,47 +65,50 @@ loop() ->
|
||||
loop()
|
||||
end.
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(local_send_to_resource_hook,
|
||||
stop(Host) ->
|
||||
ejabberd_hooks:delete(local_send_to_resource_hook, Host,
|
||||
?MODULE, announce, 50),
|
||||
ejabberd_hooks:delete(sm_register_connection_hook,
|
||||
ejabberd_hooks:delete(sm_register_connection_hook, Host,
|
||||
?MODULE, send_motd, 50),
|
||||
exit(whereis(?PROCNAME), stop),
|
||||
{wait, ?PROCNAME}.
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
exit(whereis(Proc), stop),
|
||||
{wait, Proc}.
|
||||
|
||||
announce(From, To, Packet) ->
|
||||
case To of
|
||||
#jid{luser = "", lresource = Res} ->
|
||||
{xmlelement, Name, _Attrs, _Els} = Packet,
|
||||
case {Res, Name} of
|
||||
{"announce/all", "message"} ->
|
||||
?PROCNAME ! {announce_all, From, To, Packet},
|
||||
stop;
|
||||
{"announce/online", "message"} ->
|
||||
?PROCNAME ! {announce_online, From, To, Packet},
|
||||
stop;
|
||||
{"announce/all-hosts/online", "message"} ->
|
||||
?PROCNAME ! {announce_all_hosts_online, From, To, Packet},
|
||||
stop;
|
||||
{"announce/motd", "message"} ->
|
||||
?PROCNAME ! {announce_motd, From, To, Packet},
|
||||
stop;
|
||||
{"announce/motd/update", "message"} ->
|
||||
?PROCNAME ! {announce_motd_update, From, To, Packet},
|
||||
stop;
|
||||
{"announce/motd/delete", "message"} ->
|
||||
?PROCNAME ! {announce_motd_delete, From, To, Packet},
|
||||
stop;
|
||||
_ ->
|
||||
ok
|
||||
Proc = gen_mod:get_module_proc(To#jid.lserver, ?PROCNAME),
|
||||
case {Res, Name} of
|
||||
{"announce/all", "message"} ->
|
||||
Proc ! {announce_all, From, To, Packet},
|
||||
stop;
|
||||
{"announce/online", "message"} ->
|
||||
Proc ! {announce_online, From, To, Packet},
|
||||
stop;
|
||||
{"announce/all-hosts/online", "message"} ->
|
||||
Proc ! {announce_all_hosts_online, From, To, Packet},
|
||||
stop;
|
||||
{"announce/motd", "message"} ->
|
||||
Proc ! {announce_motd, From, To, Packet},
|
||||
stop;
|
||||
{"announce/motd/update", "message"} ->
|
||||
Proc ! {announce_motd_update, From, To, Packet},
|
||||
stop;
|
||||
{"announce/motd/delete", "message"} ->
|
||||
Proc ! {announce_motd_delete, From, To, Packet},
|
||||
stop;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
announce_all(From, To, Packet) ->
|
||||
Access = gen_mod:get_module_opt(?MODULE, access, none),
|
||||