Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/processone/ejabberd.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/acl.erl20
-rw-r--r--src/ejabberd.erl37
-rw-r--r--src/ejabberd.hrl6
-rw-r--r--src/ejabberd_admin.erl4
-rw-r--r--src/ejabberd_app.erl9
-rw-r--r--src/ejabberd_auth.erl45
-rw-r--r--src/ejabberd_auth_ldap.erl116
-rw-r--r--src/ejabberd_auth_odbc.erl11
-rw-r--r--src/ejabberd_c2s.erl2
-rw-r--r--src/ejabberd_check.erl2
-rw-r--r--src/ejabberd_config.erl115
-rw-r--r--src/ejabberd_config.hrl5
-rw-r--r--src/ejabberd_hooks.erl54
-rw-r--r--src/ejabberd_local.erl27
-rw-r--r--src/ejabberd_rdbms.erl59
-rw-r--r--src/ejabberd_router.erl190
-rw-r--r--src/ejabberd_sm.erl30
-rw-r--r--src/ejabberd_sup.erl18
-rw-r--r--src/ejabberdctl.template22
-rw-r--r--src/ejd2odbc.erl2
-rw-r--r--src/gen_mod.erl17
-rw-r--r--src/odbc/ejabberd_odbc.erl16
-rw-r--r--src/odbc/ejabberd_odbc_sup.erl11
-rw-r--r--src/odbc/mysql.sql69
-rw-r--r--src/odbc/odbc_queries.erl249
-rw-r--r--src/odbc/pg.sql81
-rw-r--r--src/randoms.erl7
-rw-r--r--src/web/ejabberd_web_admin.erl2
28 files changed, 858 insertions, 368 deletions
diff --git a/src/acl.erl b/src/acl.erl
index fae1ef5ef..cae7f9bac 100644
--- a/src/acl.erl
+++ b/src/acl.erl
@@ -32,10 +32,12 @@
add/3,
add_list/3,
match_rule/3,
+ for_host/1,
% for debugging only
match_acl/3]).
-include("ejabberd.hrl").
+-include_lib("stdlib/include/ms_transform.hrl").
%% @type aclspec() = all | JID_Exact | JID_Regexp | JID_Glob | Shared_Group
%% JID_Exact = {user, U} | {user, U, S} | {server, S} | {resource, R}
@@ -234,7 +236,7 @@ match_acl(ACLName, JID, Host) ->
User = exmpp_jid:prep_node_as_list(JID),
Server = exmpp_jid:prep_domain_as_list(JID),
Resource = exmpp_jid:prep_resource_as_list(JID),
- lists:any(fun(#acl{aclspec = Spec}) ->
+ lists:any(fun(#acl{aclname=Name, aclspec = Spec}) ->
case Spec of
all ->
true;
@@ -243,17 +245,21 @@ match_acl(ACLName, JID, Host) ->
andalso
((Host == Server) orelse
((Host == global) andalso
- lists:member(Server, ?MYHOSTS)));
+ ?IS_MY_HOST(Server)));
{user, U, S} ->
(U == User) andalso (S == Server);
{server, S} ->
S == Server;
{resource, R} ->
R == Resource;
+ {user_regexp, UR} when is_tuple(Name),
+ element(2, Name) =:= global ->
+ ?IS_MY_HOST(Server)
+ andalso is_regexp_match(User, UR);
{user_regexp, UR} ->
((Host == Server) orelse
((Host == global) andalso
- lists:member(Server, ?MYHOSTS)))
+ ?IS_MY_HOST(Server)))
andalso is_regexp_match(User, UR);
{shared_group, G} ->
mod_shared_roster:is_user_in_group({User, Server}, G, Host);
@@ -272,7 +278,7 @@ match_acl(ACLName, JID, Host) ->
{user_glob, UR} ->
((Host == Server) orelse
((Host == global) andalso
- lists:member(Server, ?MYHOSTS)))
+ ?IS_MY_HOST(Server)))
andalso
is_glob_match(User, UR);
{user_glob, UR, S} ->
@@ -325,3 +331,9 @@ is_glob_match(String, Glob) ->
is_regexp_match(String, xmerl_regexp:sh_to_awk(Glob)).
+for_host(Host) ->
+ mnesia:select(acl,
+ ets:fun2ms(fun (#acl{aclname = {_ACLName, H}})
+ when H =:= Host ->
+ object()
+ end)).
diff --git a/src/ejabberd.erl b/src/ejabberd.erl
index c92509e7a..5dc5c990d 100644
--- a/src/ejabberd.erl
+++ b/src/ejabberd.erl
@@ -28,8 +28,14 @@
-author('alexey@process-one.net').
-export([start/0, stop/0,
- get_pid_file/0,
- get_so_path/0, get_bin_path/0]).
+ get_so_path/0,
+ get_bin_path/0,
+ get_pid_file/0,
+ is_my_host/1,
+ normalize_host/1
+ ]).
+
+-include("ejabberd.hrl").
start() ->
%%ejabberd_cover:start(),
@@ -65,6 +71,33 @@ get_bin_path() ->
Path
end.
+is_my_host([$* | _]) ->
+ false;
+is_my_host(Host) ->
+ case ejabberd_config:get_local_option({Host, host}) of
+ undefined ->
+ WCHost = re:replace(Host, "^[^.]*\.", "*.", [{return, list}]),
+ case ejabberd_config:get_local_option({WCHost, host}) of
+ undefined ->
+ false;
+ _ ->
+ true
+ end;
+ _ ->
+ true
+ end.
+
+normalize_host(global) ->
+ global;
+normalize_host(Host) ->
+ WCHost = re:replace(Host, "^[^.]*\.", "*.", [{return, list}]),
+ case ejabberd_config:get_local_option({WCHost, host}) of
+ undefined ->
+ Host;
+ _ ->
+ WCHost
+ end.
+
%% @spec () -> false | string()
get_pid_file() ->
case os:getenv("EJABBERD_PID_PATH") of
diff --git a/src/ejabberd.hrl b/src/ejabberd.hrl
index e1f0cfd37..4317c33a1 100644
--- a/src/ejabberd.hrl
+++ b/src/ejabberd.hrl
@@ -23,7 +23,8 @@
%% If the ejabberd application description isn't loaded, returns atom: undefined
-define(VERSION, element(2, application:get_key(ejabberd,vsn))).
--define(MYHOSTS, ejabberd_config:get_global_option(hosts)).
+-define(IS_MY_HOST(Host), ejabberd:is_my_host(Host)).
+-define(MYHOSTS, ejabberd_config:get_global_option(hosts)). %% Deprecated
-define(MYNAME, hd(ejabberd_config:get_global_option(hosts))).
-define(MYLANG, ejabberd_config:get_global_option(language)).
@@ -35,6 +36,8 @@
-define(S2STIMEOUT, 600000).
+-define(PRIVACY_SUPPORT, true).
+
%%-define(DBGFSM, true).
%% ---------------------------------
@@ -58,4 +61,3 @@
-define(CRITICAL_MSG(Format, Args),
ejabberd_logger:critical_msg(?MODULE,?LINE,Format, Args)).
-
diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl
index a38cee740..96176d8da 100644
--- a/src/ejabberd_admin.erl
+++ b/src/ejabberd_admin.erl
@@ -278,8 +278,8 @@ send_service_message_all_mucs(Subject, AnnouncementText) ->
Message = io_lib:format("~s~n~s", [Subject, AnnouncementText]),
lists:foreach(
fun(ServerHost) ->
- MUCHost = gen_mod:get_module_opt_host(
- ServerHost, mod_muc, "conference.@HOST@"),
+ MUCHost = gen_mod:expand_host_name(
+ ServerHost, mod_muc, "conference"),
MUCHostB = list_to_binary(MUCHost),
mod_muc:broadcast_service_message(MUCHostB, Message)
end,
diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl
index 1b0289752..f6379e75f 100644
--- a/src/ejabberd_app.erl
+++ b/src/ejabberd_app.erl
@@ -119,6 +119,15 @@ db_init() ->
%% Start all the modules in all the hosts
start_modules() ->
+ case ejabberd_config:get_local_option({static_modules, global}) of
+ undefined ->
+ ok;
+ StaticModules ->
+ lists:foreach(
+ fun({Module, Args}) ->
+ gen_mod:start_module(global, Module, Args)
+ end, StaticModules)
+ end,
lists:foreach(
fun(Host) ->
case ejabberd_config:get_local_option({modules, Host}) of
diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl
index 00fb0338d..c926aaeee 100644
--- a/src/ejabberd_auth.erl
+++ b/src/ejabberd_auth.erl
@@ -52,6 +52,15 @@
plain_password_required/1
]).
+-export([start/1
+ ,start_module/2
+ ,stop_module/2
+ ,start_modules/2
+ ,start_method/2
+ ,stop_method/2
+ ,start_methods/2
+ ]).
+
-export([auth_modules/1]).
-include("ejabberd.hrl").
@@ -67,13 +76,26 @@
%% @spec () -> term()
start() ->
- lists:foreach(
- fun(Host) ->
- lists:foreach(
- fun(M) ->
- M:start(Host)
- end, auth_modules(Host))
- end, ?MYHOSTS).
+ ?DEBUG("About to start auth modules. Hosts: ~p", ?MYHOSTS),
+ lists:foreach(fun start/1, ?MYHOSTS).
+
+start(Host) ->
+ start_modules(Host, auth_modules(Host)).
+
+start_modules(Host, Modules) when is_list(Modules) ->
+ lists:foreach(fun (M) -> start_module(Host, M) end, Modules).
+start_module(Host, Module) when is_atom(Module) ->
+ Module:start(Host).
+stop_module(Host, Module) when is_atom(Module) ->
+ Module:stop(Host).
+
+start_methods(Host, Methods) when is_list(Methods) ->
+ lists:foreach(fun (M) -> start_method(Host, M) end, Methods).
+start_method(Host, Method) when is_atom(Method) ->
+ start_module(Host, module_name(Method)).
+stop_method(Host, Method) when is_atom(Method) ->
+ stop_module(Host, module_name(Method)).
+
%% @spec (Server) -> bool()
%% Server = string()
@@ -186,7 +208,7 @@ try_register(User, Server, Password)
true ->
{atomic, exists};
false ->
- case lists:member(exmpp_stringprep:nameprep(Server), ?MYHOSTS) of
+ case ?IS_MY_HOST(exmpp_stringprep:nameprep(Server)) of
true ->
Res = lists:foldl(
fun (_M, {atomic, ok} = Res) ->
@@ -443,10 +465,13 @@ auth_modules() ->
auth_modules(Server) when is_list(Server) ->
LServer = exmpp_stringprep:nameprep(Server),
- Method = ejabberd_config:get_local_option({auth_method, LServer}),
+ Method = ejabberd_config:get_local_option({auth_method, ejabberd:normalize_host(LServer)}),
Methods = if
Method == undefined -> [];
is_list(Method) -> Method;
is_atom(Method) -> [Method]
end,
- [list_to_atom("ejabberd_auth_" ++ atom_to_list(M)) || M <- Methods].
+ [module_name(M) || M <- Methods].
+
+module_name(Method) when is_atom(Method) ->
+ list_to_atom("ejabberd_auth_" ++ atom_to_list(Method)).
diff --git a/src/ejabberd_auth_ldap.erl b/src/ejabberd_auth_ldap.erl
index 6fb6d2faf..54419374e 100644
--- a/src/ejabberd_auth_ldap.erl
+++ b/src/ejabberd_auth_ldap.erl
@@ -72,7 +72,6 @@
base,
uids,
ufilter,
- sfilter,
lfilter, %% Local filter (performed by ejabberd, not LDAP)
dn_filter,
dn_filter_attrs
@@ -99,21 +98,41 @@ handle_info(_Info, State) ->
%% Host = string()
start(Host) ->
- Proc = gen_mod:get_module_proc(Host, ?MODULE),
- ChildSpec = {
- Proc, {?MODULE, start_link, [Host]},
- transient, 1000, worker, [?MODULE]
- },
- supervisor:start_child(ejabberd_sup, ChildSpec).
+ ?DEBUG("Starting ~p for ~p.", [?MODULE, Host]),
+ case ejabberd_config:get_host_option(Host, ldap_servers) of
+ undefined -> check_bad_config(Host);
+ {host, _Host} -> ok;
+ _ ->
+ Proc = gen_mod:get_module_proc(Host, ?MODULE),
+ ChildSpec = {
+ Proc, {?MODULE, start_link, [Host]},
+ transient, 1000, worker, [?MODULE]
+ },
+ supervisor:start_child(ejabberd_sup, ChildSpec)
+ end.
+
+check_bad_config(Host) ->
+ case ejabberd_config:get_local_option({ldap_servers, Host}) of
+ undefined ->
+ ?ERROR_MSG("Can't start ~p for host ~p: missing ldap_servers configuration",
+ [?MODULE, Host]),
+ {error, bad_config};
+ _ -> ok
+ end.
%% @spec (Host) -> term()
%% Host = string()
stop(Host) ->
- Proc = gen_mod:get_module_proc(Host, ?MODULE),
- gen_server:call(Proc, stop),
- supervisor:terminate_child(ejabberd_sup, Proc),
- supervisor:delete_child(ejabberd_sup, Proc).
+ case ejabberd_config:get_host_option(Host, ldap_servers) of
+ undefined -> ok;
+ {host, _Host} -> ok;
+ _ ->
+ Proc = gen_mod:get_module_proc(Host, ?MODULE),
+ gen_server:call(Proc, stop),
+ supervisor:terminate_child(ejabberd_sup, Proc),
+ supervisor:delete_child(ejabberd_sup, Proc)
+ end.
%% @spec (Host) -> term()
%% Host = string()
@@ -190,7 +209,7 @@ check_password(User, Server, Password, _Digest, _DigestGen) ->
set_password(User, Server, Password) ->
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
- case find_user_dn(User, State) of
+ case find_user_dn(User, Server, State) of
false ->
{error, user_not_found};
DN ->
@@ -285,8 +304,8 @@ remove_user(_User, _Server, _Password) ->
%% Password = string()
check_password_ldap(User, Server, Password) ->
- {ok, State} = eldap_utils:get_state(Server, ?MODULE),
- case find_user_dn(User, State) of
+ {ok, State} = get_state(Server),
+ case find_user_dn(User, Server, State) of
false ->
false;
DN ->
@@ -294,7 +313,21 @@ check_password_ldap(User, Server, Password) ->
ok -> true;
_ -> false
end
- end.
+ end.
+
+%% We need an ?MODULE server state to use for queries. This will
+%% either be Server if this is a statically configured host or the
+%% Server for a different host if this is a dynamically configured
+%% vhost.
+%% The {ldap_vhost, Server} -> Host. ejabberd config option specifies
+%% which actual ?MODULE server to use for a particular Host. The value
+%% of the option if it is defined or Server by default.
+get_state(Server) ->
+ Host = case ejabberd_config:get_local_option({ldap_servers, Server}) of
+ {host, H} -> H;
+ _ -> Server
+ end,
+ eldap_utils:get_state(Host, ?MODULE).
%% @spec (Server) -> [{LUser, LServer}]
%% Server = string()
@@ -303,11 +336,11 @@ check_password_ldap(User, Server, Password) ->
get_vh_registered_users_ldap(Server) ->
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
- UIDs = State#state.uids,
+ UIDs = eldap_utils:uids_domain_subst(Server, State#state.uids),
Eldap_ID = State#state.eldap_id,
- Server = State#state.host,
+ SearchFilter = build_sfilter(State, UIDs),
ResAttrs = result_attrs(State),
- case eldap_filter:parse(State#state.sfilter) of
+ case eldap_filter:parse(SearchFilter) of
{ok, EldapFilter} ->
case eldap_pool:search(Eldap_ID, [{base, State#state.base},
{filter, EldapFilter},
@@ -317,7 +350,7 @@ get_vh_registered_users_ldap(Server) ->
lists:flatmap(
fun(#eldap_entry{attributes = Attrs,
object_name = DN}) ->
- case is_valid_dn(DN, Attrs, State) of
+ case is_valid_dn(DN, Server, Attrs, State) of
false -> [];
_ ->
case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
@@ -349,7 +382,7 @@ get_vh_registered_users_ldap(Server) ->
is_user_exists_ldap(User, Server) ->
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
- case find_user_dn(User, State) of
+ case find_user_dn(User, Server, State) of
false -> false;
_DN -> true
end.
@@ -363,16 +396,17 @@ handle_call(stop, _From, State) ->
handle_call(_Request, _From, State) ->
{reply, bad_request, State}.
-find_user_dn(User, State) ->
+find_user_dn(User, Server, State) ->
ResAttrs = result_attrs(State),
- case eldap_filter:parse(State#state.ufilter, [{"%u", User}]) of
+ UserFilter = build_ufilter(State, Server),
+ case eldap_filter:parse(UserFilter, [{"%u", User}]) of
{ok, Filter} ->
case eldap_pool:search(State#state.eldap_id, [{base, State#state.base},
{filter, Filter},
{attributes, ResAttrs}]) of
#eldap_search_result{entries = [#eldap_entry{attributes = Attrs,
object_name = DN} | _]} ->
- dn_filter(DN, Attrs, State);
+ dn_filter(DN, Server, Attrs, State);
_ ->
false
end;
@@ -381,20 +415,20 @@ find_user_dn(User, State) ->
end.
%% apply the dn filter and the local filter:
-dn_filter(DN, Attrs, State) ->
+dn_filter(DN, Server, Attrs, State) ->
%% Check if user is denied access by attribute value (local check)
case check_local_filter(Attrs, State) of
false -> false;
- true -> is_valid_dn(DN, Attrs, State)
+ true -> is_valid_dn(DN, Server, Attrs, State)
end.
%% Check that the DN is valid, based on the dn filter
-is_valid_dn(DN, _, #state{dn_filter = undefined}) ->
+is_valid_dn(DN, _, _, #state{dn_filter = undefined}) ->
DN;
-is_valid_dn(DN, Attrs, State) ->
+is_valid_dn(DN, Server, Attrs, State) ->
DNAttrs = State#state.dn_filter_attrs,
- UIDs = State#state.uids,
+ UIDs = eldap_utils:uids_domain_subst(Server, State#state.uids),
Values = [{"%s", eldap_utils:get_ldap_attr(Attr, Attrs), 1} || Attr <- DNAttrs],
SubstValues = case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
"" -> Values;
@@ -449,6 +483,22 @@ result_attrs(#state{uids = UIDs, dn_filter_attrs = DNFilterAttrs}) ->
[UID | Acc]
end, DNFilterAttrs, UIDs).
+build_ufilter(State, VHost) ->
+ UIDs = eldap_utils:uids_domain_subst(VHost, State#state.uids),
+ SubFilter = lists:flatten(eldap_utils:generate_subfilter(UIDs)),
+ case State#state.ufilter of
+ "" -> SubFilter;
+ F -> "(&" ++ SubFilter ++ F ++ ")"
+ end.
+
+build_sfilter(State, FormattedUIDs) ->
+ SubFilter = lists:flatten(eldap_utils:generate_subfilter(FormattedUIDs)),
+ UserFilter = case State#state.ufilter of
+ "" -> SubFilter;
+ F -> "(&" ++ SubFilter ++ F ++ ")"
+ end,
+ eldap_filter:do_sub(UserFilter, [{"%u", "*"}]).
+
%%%----------------------------------------------------------------------
%%% Auxiliary functions
%%%----------------------------------------------------------------------
@@ -480,15 +530,12 @@ parse_options(Host) ->
end,
UIDs = case ejabberd_config:get_local_option({ldap_uids, Host}) of
undefined -> [{"uid", "%u"}];
- UI -> eldap_utils:uids_domain_subst(Host, UI)
+ UI -> UI
end,
- SubFilter = lists:flatten(eldap_utils:generate_subfilter(UIDs)),
UserFilter = case ejabberd_config:get_local_option({ldap_filter, Host}) of
- undefined -> SubFilter;
- "" -> SubFilter;
- F -> "(&" ++ SubFilter ++ F ++ ")"
+ undefined -> "";
+ F -> F
end,
- SearchFilter = eldap_filter:do_sub(UserFilter, [{"%u", "*"}]),
LDAPBase = ejabberd_config:get_local_option({ldap_base, Host}),
{DNFilter, DNFilterAttrs} =
case ejabberd_config:get_local_option({ldap_dn_filter, Host}) of
@@ -513,7 +560,6 @@ parse_options(Host) ->
base = LDAPBase,
uids = UIDs,
ufilter = UserFilter,
- sfilter = SearchFilter,
lfilter = LocalFilter,
dn_filter = DNFilter,
dn_filter_attrs = DNFilterAttrs
diff --git a/src/ejabberd_auth_odbc.erl b/src/ejabberd_auth_odbc.erl
index 1389e1951..8227b241d 100644
--- a/src/ejabberd_auth_odbc.erl
+++ b/src/ejabberd_auth_odbc.erl
@@ -29,6 +29,7 @@
%% External exports
-export([start/1,
+ stop/1,
set_password/3,
check_password/3,
check_password/5,
@@ -55,8 +56,14 @@
%% @spec (Host) -> ok
%% Host = string()
-start(_Host) ->
- ok.
+start(Host) ->
+ case ejabberd_odbc:running(Host) of
+ true -> ok;
+ false -> ejabberd_rdbms:start_odbc(Host)
+ end.
+
+stop(Host) ->
+ ejabberd_rdbms:stop_odbc(Host).
%% @spec () -> bool()
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 3f7dde71d..010fb177f 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -315,7 +315,7 @@ wait_for_stream({xmlstreamstart, #xmlel{ns = NS} = Opening}, StateData) ->
ServerB = exmpp_stringprep:nameprep(
exmpp_stream:get_receiving_entity(Opening)),
Server = binary_to_list(ServerB),
- case lists:member(Server, ?MYHOSTS) of
+ case ?IS_MY_HOST(Server) of
true ->
Lang = exmpp_stream:get_lang(Opening),
change_shaper(StateData,
diff --git a/src/ejabberd_check.erl b/src/ejabberd_check.erl
index 403cdd2a9..8b315861a 100644
--- a/src/ejabberd_check.erl
+++ b/src/ejabberd_check.erl
@@ -46,6 +46,8 @@ config() ->
check_database_modules() ->
[check_database_module(M)||M<-get_db_used()].
+check_database_module(host) ->
+ ok;
check_database_module(odbc) ->
check_modules(odbc, [odbc, odbc_app, odbc_sup, ejabberd_odbc, ejabberd_odbc_sup, odbc_queries]);
check_database_module(mysql) ->
diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl
index 2233f7123..e9c167a87 100644
--- a/src/ejabberd_config.erl
+++ b/src/ejabberd_config.erl
@@ -27,17 +27,32 @@
-module(ejabberd_config).
-author('alexey@process-one.net').
--export([start/0, load_file/1,
+-export([start/0, load_file/1, get_host_option/2,
add_global_option/2, add_local_option/2,
+ mne_add_local_option/2, mne_del_local_option/1,
del_global_option/1, del_local_option/1,
get_global_option/1, get_local_option/1]).
+
+-export([for_host/1
+ ,configure_host/2
+ ,delete_host/1
+ ]).
+
+-export([search/1]).
+
-export([get_vh_by_auth_method/1]).
-export([is_file_readable/1]).
-include("ejabberd.hrl").
-include("ejabberd_config.hrl").
-include_lib("kernel/include/file.hrl").
+-include_lib("stdlib/include/ms_transform.hrl").
+-record(state, {opts = [],
+ hosts = [],
+ override_local = false,
+ override_global = false,
+ override_acls = false}).
%% @type macro() = {macro_key(), macro_value()}
@@ -153,6 +168,14 @@ search_hosts(Term, State) ->
add_hosts_to_option(Hosts, State) ->
PrepHosts = normalize_hosts(Hosts),
+ mnesia:transaction(
+ fun() ->
+ lists:foreach(
+ fun(H) ->
+ mnesia:write(#local_config{key = {H, host},
+ value = []})
+ end, PrepHosts)
+ end),
add_option(hosts, PrepHosts, State#state{hosts = PrepHosts}).
normalize_hosts(Hosts) ->
@@ -379,6 +402,8 @@ process_term(Term, State) ->
{host_config, Host, Terms} ->
lists:foldl(fun(T, S) -> process_host_term(T, Host, S) end,
State, Terms);
+ {clusterid, ClusterID} ->
+ add_option(clusterid, ClusterID, State);
{listen, Listeners} ->
Listeners2 =
lists:map(
@@ -443,8 +468,7 @@ process_term(Term, State) ->
{max_fsm_queue, N} ->
add_option(max_fsm_queue, N, State);
{_Opt, _Val} ->
- lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
- State, State#state.hosts)
+ process_host_term(Term, global, State)
end.
process_host_term(Term, Host, State) ->
@@ -569,10 +593,11 @@ add_global_option(Opt, Val) ->
end).
add_local_option(Opt, Val) ->
- mnesia:transaction(fun() ->
- mnesia:write(#local_config{key = Opt,
- value = Val})
- end).
+ mnesia:transaction(fun mne_add_local_option/2, [Opt, Val]).
+
+mne_add_local_option(Opt, Val) ->
+ mnesia:write(#local_config{key = Opt,
+ value = Val}).
del_global_option(Opt) ->
mnesia:transaction(fun() ->
@@ -585,6 +610,13 @@ del_local_option(Opt) ->
end).
+get_global_option({Opt1, Host} = Opt) when is_list(Host) ->
+ case ets:lookup(config, Opt) of
+ [#config{value = Val}] ->
+ Val;
+ _ ->
+ get_global_option({Opt1, global})
+ end;
get_global_option(Opt) ->
case ets:lookup(config, Opt) of
[#config{value = Val}] ->
@@ -593,6 +625,13 @@ get_global_option(Opt) ->
undefined
end.
+get_local_option({Opt1, Host} = Opt) when is_list(Host) ->
+ case ets:lookup(local_config, Opt) of
+ [#local_config{value = Val}] ->
+ Val;
+ _ ->
+ get_local_option({Opt1, global})
+ end;
get_local_option(Opt) ->
case ets:lookup(local_config, Opt) of
[#local_config{value = Val}] ->
@@ -601,6 +640,17 @@ get_local_option(Opt) ->
undefined
end.
+get_host_option(Host, Option) ->
+ case ets:lookup(local_config, {Option, Host}) of
+ [#local_config{value=V}] -> V;
+ _ -> undefined
+ end.
+
+mne_del_local_option({_OptName, Host} = Opt) when is_list(Host) ->
+ mnesia:delete({local_config, Opt});
+mne_del_local_option({Host, host} = Opt) when is_list(Host) ->
+ mnesia:delete({local_config, Opt}).
+
%% Return the list of hosts handled by a given module
get_vh_by_auth_method(AuthMethod) ->
mnesia:dirty_select(local_config,
@@ -619,3 +669,54 @@ is_file_readable(Path) ->
{error, _Reason} ->
false
end.
+
+search(Pattern) ->
+ {atomic, Res} = mnesia:transaction(fun mnesia:select/2, [local_config, Pattern]),
+ Res.
+
+for_host(Host) ->
+ mnesia:read({local_config, {Host, host}})
+ ++ mnesia:select(local_config,
+ ets:fun2ms(fun (#local_config{key={_, H}})
+ when H =:= Host ->
+ object()
+ end))
+ ++ acl:for_host(Host).
+
+delete_host(Host) ->
+ mnesia_delete_objects(for_host(Host)),
+ ok.
+
+configure_host(Host, Config) ->
+ HostExistenceTerm = {{Host, host}, []},
+ Records = host_terms_to_records(Host, [HostExistenceTerm | Config]),
+ mnesia_write_objects(Records),
+ ok.
+
+host_terms_to_records(Host, Terms) ->
+ lists:foldl(fun (Term, Acc) ->
+ host_term_to_record(Term, Host, Acc)
+ end, [], Terms).
+
+host_term_to_record({acl, ACLName, ACLData}, Host, Acc) ->
+ [acl:to_record(Host, ACLName, ACLData) | Acc];
+host_term_to_record({access, RuleName, Rules}, Host, Acc) ->
+ [#config{key={access, RuleName, Host}, value=Rules} | Acc];
+host_term_to_record({shaper, Name, Data}, Host, Acc) ->
+ [#config{key={shaper, Name, Host}, value=Data} | Acc];
+host_term_to_record({host, _}, _Host, Acc) -> Acc;
+host_term_to_record({hosts, _}, _Host, Acc) -> Acc;
+host_term_to_record({{Host, host}, []}, Host, Acc) ->
+ [#local_config{key={Host, host}, value=[]} | Acc];
+host_term_to_record({Opt, Val}, Host, Acc) when is_atom(Opt) ->
+ [#local_config{key={Opt, Host}, value=Val} | Acc].
+
+
+mnesia_delete_objects(List) when is_list(List) ->
+ true = lists:all(fun (I) ->
+ ok =:= mnesia:delete_object(I)
+ end, List).
+mnesia_write_objects(List) when is_list(List) ->
+ true = lists:all(fun (I) ->
+ ok =:= mnesia:write(I)
+ end, List).
diff --git a/src/ejabberd_config.hrl b/src/ejabberd_config.hrl
index f847f35b7..c9c5335f7 100644
--- a/src/ejabberd_config.hrl
+++ b/src/ejabberd_config.hrl
@@ -21,8 +21,3 @@
-record(config, {key, value}).
-record(local_config, {key, value}).
--record(state, {opts = [],
- hosts = [],
- override_local = false,
- override_global = false,
- override_acls = false}).
diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl
index e88df4548..58933e964 100644
--- a/src/ejabberd_hooks.erl
+++ b/src/ejabberd_hooks.erl
@@ -119,14 +119,27 @@ delete_dist(Hook, Host, Node, Module, Function, Seq) ->
%% @doc Run the calls of this hook in order, don't care about function results.
%% If a call returns stop, no more calls are performed.
run(Hook, Args) ->
- run(Hook, global, Args).
+ runx(Hook, global, Args).
-run(Hook, Host, Args) when is_binary(Host) orelse is_atom(Host) ->
- case ets:lookup(hooks, {Hook, Host}) of
+run(Hook, Host, Args) when is_binary(Host) ->
+ case runx(Hook, Host, Args) of
+ stop -> stop;
+ _ -> runx(Hook, global, Args)
+ end;
+run(Hook, Host, Args) when Host == global ->
+ runx(Hook, Host, Args).
+
+runx(Hook, Host, Args) when is_binary(Host) orelse is_atom(Host) ->
+ case ets:lookup(hooks, {Hook, ejabberd:normalize_host(Host)}) of
[{_, Ls}] ->
run1(Ls, Hook, Args);
[] ->
- ok
+ case ets:lookup(hooks, {Hook, global}) of
+ [{_, Ls}] ->
+ run1(Ls, Hook, Args);
+ [] ->
+ ok
+ end
end.
%% @spec (Hook::atom(), Val, Args) -> Val | stopped | NewVal
@@ -136,16 +149,29 @@ run(Hook, Host, Args) when is_binary(Host) orelse is_atom(Host) ->
%% If a call returns 'stop', no more calls are performed and 'stopped' is returned.
%% If a call returns {stopped, NewVal}, no more calls are performed and NewVal is returned.
run_fold(Hook, Val, Args) ->
- run_fold(Hook, global, Val, Args).
+ run_foldx(Hook, global, Val, Args).
%% @spec (Hook::atom(), Host, Val, Args) -> Val | stopped | NewVal
%% Host = global | binary()
-run_fold(Hook, Host, Val, Args) when is_binary(Host) orelse is_atom(Host) ->
- case ets:lookup(hooks, {Hook, Host}) of
+run_fold(Hook, Host, Val, Args) when is_binary(Host) ->
+ case run_foldx(Hook, Host, Val, Args) of
+ stopped -> stopped;
+ Val2 -> run_foldx(Hook, global, Val2, Args)
+ end;
+run_fold(Hook, Host, Val, Args) when Host == global ->
+ run_foldx(Hook, Host, Val, Args).
+
+run_foldx(Hook, Host, Val, Args) ->
+ case ets:lookup(hooks, {Hook, ejabberd:normalize_host(Host)}) of
[{_, Ls}] ->
run_fold1(Ls, Hook, Val, Args);
[] ->
- Val
+ case ets:lookup(hooks, {Hook, global}) of
+ [{_, Ls}] ->
+ run_fold1(Ls, Hook, Val, Args);
+ [] ->
+ Val
+ end
end.
%%%----------------------------------------------------------------------
@@ -173,7 +199,8 @@ init([]) ->
%% {stop, Reason, State} (terminate/2 is called)
%%----------------------------------------------------------------------
handle_call({add, Hook, Host, Module, Function, Seq}, _From, State) ->
- Reply = case ets:lookup(hooks, {Hook, Host}) of
+ NHost = ejabberd:normalize_host(Host),
+ Reply = case ets:lookup(hooks, {Hook, NHost}) of
[{_, Ls}] ->
El = {Seq, Module, Function},
case lists:member(El, Ls) of
@@ -181,12 +208,12 @@ handle_call({add, Hook, Host, Module, Function, Seq}, _From, State) ->
ok;
false ->
NewLs = lists:merge(Ls, [El]),
- ets:insert(hooks, {{Hook, Host}, NewLs}),
+ ets:insert(hooks, {{Hook, NHost}, NewLs}),
ok
end;
[] ->
NewLs = [{Seq, Module, Function}],
- ets:insert(hooks, {{Hook, Host}, NewLs}),
+ ets:insert(hooks, {{Hook, NHost}, NewLs}),
ok
end,
{reply, Reply, State};
@@ -209,10 +236,11 @@ handle_call({add, Hook, Host, Node, Module, Function, Seq}, _From, State) ->
end,
{reply, Reply, State};
handle_call({delete, Hook, Host, Module, Function, Seq}, _From, State) ->
- Reply = case ets:lookup(hooks, {Hook, Host}) of
+ NHost = ejabberd:normalize_host(Host),
+ Reply = case ets:lookup(hooks, {Hook, NHost}) of
[{_, Ls}] ->
NewLs = lists:delete({Seq, Module, Function}, Ls),
- ets:insert(hooks, {{Hook, Host}, NewLs}),
+ ets:insert(hooks, {{Hook, NHost}, NewLs}),
ok;
[] ->
ok
diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl
index 65086d972..ebcad372d 100644
--- a/src/ejabberd_local.erl
+++ b/src/ejabberd_local.erl
@@ -82,7 +82,7 @@ process_iq(From, To, Packet) ->
case exmpp_iq:xmlel_to_iq(Packet) of
#iq{kind = request, ns = XMLNS} = IQ_Rec ->
Host = exmpp_jid:prep_domain(To),
- case ets:lookup(?IQTABLE, {XMLNS, Host}) of
+ case ets:lookup(?IQTABLE, {XMLNS, ejabberd:normalize_host(Host)}) of
[{_, Module, Function}] ->
ResIQ = Module:Function(From, To, IQ_Rec),
if
@@ -96,8 +96,15 @@ process_iq(From, To, Packet) ->
gen_iq_handler:handle(Host, Module, Function, Opts,
From, To, IQ_Rec);
[] ->
- Err = exmpp_iq:error(Packet, 'feature-not-implemented'),
- ejabberd_router:route(To, From, Err)
+ case ets:lookup(?IQTABLE, {XMLNS, global}) of
+ [{_, Module, Function, Opts}] ->
+ gen_iq_handler:handle(
+ global, Module, Function, Opts,
+ From, To, IQ_Rec);
+ [] ->
+ Err = exmpp_iq:error(Packet, 'feature-not-implemented'),
+ ejabberd_router:route(To, From, Err)
+ end
end;
#iq{kind = response} = IQReply ->
%%IQReply = jlib:iq_query_or_response_info(IQ_Rec),
@@ -193,10 +200,10 @@ bounce_resource_packet(From, To, Packet) ->
init([]) ->
lists:foreach(
fun(Host) ->
- ejabberd_router:register_route(Host, {apply, ?MODULE, route}),
- ejabberd_hooks:add(local_send_to_resource_hook, list_to_binary(Host),
- ?MODULE, bounce_resource_packet, 100)
+ ejabberd_router:register_route(Host, {apply, ?MODULE, route})
end, ?MYHOSTS),
+ ejabberd_hooks:add(local_send_to_resource_hook, global,
+ ?MODULE, bounce_resource_packet, 100),
catch ets:new(?IQTABLE, [named_table, public]),
mnesia:delete_table(iq_response),
catch ets:new(iq_response, [named_table, public,
@@ -253,21 +260,21 @@ handle_info({route, From, To, Packet}, State) ->
end,
{noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) ->
- ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function}),
+ ets:insert(?IQTABLE, {{XMLNS, ejabberd:normalize_host(Host)}, Module, Function}),
catch mod_disco:register_feature(Host, XMLNS),
{noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) ->
- ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function, Opts}),
+ ets:insert(?IQTABLE, {{XMLNS, ejabberd:normalize_host(Host)}, Module, Function, Opts}),
catch mod_disco:register_feature(Host, XMLNS),
{noreply, State};
handle_info({unregister_iq_handler, Host, XMLNS}, State) ->
- case ets:lookup(?IQTABLE, {XMLNS, Host}) of
+ case ets:lookup(?IQTABLE, {XMLNS, ejabberd:normalize_host(Host)}) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ ->
ok
end,
- ets:delete(?IQTABLE, {XMLNS, Host}),
+ ets:delete(?IQTABLE, {XMLNS, ejabberd:normalize_host(Host)}),
catch mod_disco:unregister_feature(Host, XMLNS),
{noreply, State};
handle_info(refresh_iq_handlers, State) ->
diff --git a/src/ejabberd_rdbms.erl b/src/ejabberd_rdbms.erl
index 3b5d748b4..2cb67b3a2 100644
--- a/src/ejabberd_rdbms.erl
+++ b/src/ejabberd_rdbms.erl
@@ -27,8 +27,15 @@
-module(ejabberd_rdbms).
-author('alexey@process-one.net').
--export([start/0]).
+-export([start/0
+ ,start_odbc/1
+ ,stop_odbc/1
+ ,running/1
+ ]).
-include("ejabberd.hrl").
+-include("ejabberd_config.hrl").
+-include_lib("stdlib/include/ms_transform.hrl").
+-define(SUPERVISOR, ejabberd_sup).
start() ->
%% Check if ejabberd has been compiled with ODBC
@@ -52,22 +59,39 @@ start_hosts() ->
%% Start the ODBC module on the given host
start_odbc(Host) ->
- Supervisor_name = gen_mod:get_module_proc(Host, ejabberd_odbc_sup),
+ SupervisorName = sup_name(Host),
ChildSpec =
- {Supervisor_name,
+ {SupervisorName,
{ejabberd_odbc_sup, start_link, [Host]},
transient,
infinity,
supervisor,
[ejabberd_odbc_sup]},
- case supervisor:start_child(ejabberd_sup, ChildSpec) of
+ case supervisor:start_child(?SUPERVISOR, ChildSpec) of
{ok, _PID} ->
ok;
_Error ->
- ?ERROR_MSG("Start of supervisor ~p failed:~n~p~nRetrying...~n", [Supervisor_name, _Error]),
+ ?ERROR_MSG("Start of supervisor ~p failed:~n~p~nRetrying...~n", [SupervisorName, _Error]),
start_odbc(Host)
end.
+stop_odbc(Host) ->
+ SupervisorName = sup_name(Host),
+ case running(Host) of
+ false -> ok;
+ true ->
+ case [H || H <- dependent_hosts(Host), ejabberd_hosts:running(H)] of
+ [] ->
+ ?INFO_MSG("About to terminate ~p", [SupervisorName]),
+ ok = supervisor:terminate_child(?SUPERVISOR, SupervisorName),
+ ok = supervisor:delete_child(?SUPERVISOR, SupervisorName);
+ RunningHosts ->
+ ?WARNING_MSG("Not stopping ODBC for ~p because the virtual hosts ~p are still using it.",
+ [Host, RunningHosts]),
+ {error, still_in_use}
+ end
+ end.
+
%% Returns true if we have configured odbc_server for the given host
needs_odbc(Host) ->
try
@@ -75,9 +99,32 @@ needs_odbc(Host) ->
case ejabberd_config:get_local_option({odbc_server, LHost}) of
undefined ->
false;
- _ -> true
+ {host, _} ->
+ false;
+ _ ->
+ true
end
catch
_ ->
false
end.
+
+running(Host) ->
+ Supervisors = supervisor:which_children(?SUPERVISOR),
+ SupervisorName = gen_mod:get_module_proc(Host, ejabberd_odbc_sup),
+ case lists:keysearch(SupervisorName, 1, Supervisors) of
+ false -> false;
+ {value, Cspec} when is_tuple(Cspec) -> true
+ end.
+
+
+dependent_hosts(Host) ->
+ MS = ets:fun2ms(fun (#local_config{key={odbc_server, DHost},
+ value={host, H}})
+ when H =:= Host ->
+ DHost
+ end),
+ ejabberd_config:search(MS).
+
+sup_name(Host) ->
+ gen_mod:get_module_proc(Host, ejabberd_odbc_sup).
diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl
index d6927f398..3bf0439ba 100644
--- a/src/ejabberd_router.erl
+++ b/src/ejabberd_router.erl
@@ -36,9 +36,11 @@
register_route/2,
register_routes/1,
unregister_route/1,
+ force_unregister_route/1,
unregister_routes/1,
dirty_get_all_routes/0,
dirty_get_all_domains/0,
+ read_route/1,
make_id/0
]).
@@ -98,56 +100,24 @@ route_error(From, To, ErrPacket, OrigPacket) ->
ok
end.
-register_route(Domain) ->
+register_route({global, Prefix}) ->
+ ejabberd_global_router:register_route(Prefix);
+register_route(Domain) when is_list(Domain) ->
register_route(Domain, undefined).
register_route(Domain, LocalHint) ->
try
- LDomain = exmpp_stringprep:nameprep(Domain),
- LDomainB = list_to_binary(LDomain),
- Pid = self(),
- case get_component_number(LDomain) of
- undefined ->
- F = fun() ->
- mnesia:write(#route{domain = LDomainB,
- pid = Pid,
- local_hint = LocalHint})
- end,
- mnesia:transaction(F);
- N ->
- F = fun() ->
- case mnesia:wread({route, LDomainB}) of
- [] ->
- mnesia:write(
- #route{domain = LDomainB,
- pid = Pid,
- local_hint = 1}),
- lists:foreach(
- fun(I) ->
- mnesia:write(
- #route{domain = LDomainB,
- pid = undefined,
- local_hint = I})
- end, lists:seq(2, N));
- Rs ->
- lists:any(
- fun(#route{pid = undefined,
- local_hint = I} = R) ->
- mnesia:write(
- #route{domain = LDomainB,
- pid = Pid,
- local_hint = I}),
- mnesia:delete_object(R),
- true;
- (_) ->
- false
- end, Rs)
- end
- end,
- mnesia:transaction(F)
- end
+ LDomain = exmpp_stringprep:nameprep(Domain),
+ LDomainB = list_to_binary(LDomain),
+ Pid = self(),
+ case get_component_number(LDomain) of
+ undefined ->
+ mnesia:transaction(fun register_simple_route/3, [LDomainB, Pid, LocalHint]);
+ N ->
+ mnesia:transaction(fun register_balanced_route/3, [LDomainB, Pid, N])
+ end
catch
- _ ->
+ _ ->
erlang:error({invalid_domain, Domain})
end.
@@ -156,53 +126,112 @@ register_routes(Domains) ->
register_route(Domain)
end, Domains).
-unregister_route(Domain) ->
+register_simple_route(LDomain, Pid, LocalHint) ->
+ mnesia:write(#route{domain = LDomain,
+ pid = Pid,
+ local_hint = LocalHint}).
+
+register_balanced_route(LDomain, Pid, N) ->
+ case mnesia:read({route, LDomain}) of
+ [] ->
+ mnesia:write(
+ #route{domain = LDomain,
+ pid = Pid,
+ local_hint = 1}),
+ lists:foreach(
+ fun(I) ->
+ mnesia:write(
+ #route{domain = LDomain,
+ pid = undefined,
+ local_hint = I})
+ end, lists:seq(2, N));
+ Rs ->
+ lists:any(
+ fun(#route{pid = undefined,
+ local_hint = I} = R) ->
+ mnesia:write(
+ #route{domain = LDomain,
+ pid = Pid,
+ local_hint = I}),
+ mnesia:delete_object(R),
+ true;
+ (_) ->
+ false
+ end, Rs)
+ end.
+
+unregister_route({global, Prefix}) ->
+ ejabberd_global_router:unregister_route(Prefix);
+unregister_route(Domain) when is_list(Domain) ->
try
LDomain = exmpp_stringprep:nameprep(Domain),
LDomainB = list_to_binary(LDomain),
Pid = self(),
case get_component_number(LDomain) of
undefined ->
- F = fun() ->
- case mnesia:match_object(
- #route{domain = LDomainB,
- pid = Pid,
- _ = '_'}) of
- [R] ->
- mnesia:delete_object(R);
- _ ->
- ok
- end
- end,
- mnesia:transaction(F);
+ mnesia:transaction(fun delete_simple_route/2, [LDomainB, Pid]);
_ ->
- F = fun() ->
- case mnesia:match_object(#route{domain=LDomainB,
- pid = Pid,
- _ = '_'}) of
- [R] ->
- I = R#route.local_hint,
- mnesia:write(
- #route{domain = LDomainB,
- pid = undefined,
- local_hint = I}),
- mnesia:delete_object(R);
- _ ->
- ok
- end
- end,
- mnesia:transaction(F)
+ mnesia:transaction(fun delete_balanced_route/2, [LDomainB, Pid])
end
catch
_ ->
erlang:error({invalid_domain, Domain})
end.
+delete_simple_route(LDomain, Pid) ->
+ case mnesia:match_object(#route{domain = LDomain,
+ pid = Pid,
+ _ = '_'}) of
+ [R] ->
+ mnesia:delete_object(R);
+ _ ->
+ ok
+ end.
+
+delete_balanced_route(LDomain, Pid) ->
+ case mnesia:match_object(#route{domain=LDomain,
+ pid = Pid,
+ _ = '_'}) of
+ [R] ->
+ I = R#route.local_hint,
+ ok = mnesia:write(
+ #route{domain = LDomain,
+ pid = undefined,
+ local_hint = I}),
+ mnesia:delete_object(R);
+ _ ->
+ ok
+ end.
+
+
+force_unregister_route(Domain) ->
+ case jlib:nameprep(Domain) of
+ error ->
+ erlang:error({invalid_domain, Domain});
+ LDomain ->
+ F = fun() ->
+ case mnesia:match_object(
+ #route{domain = LDomain,
+ _ = '_'}) of
+ Rs when is_list(Rs) ->
+ lists:foreach(fun(R) ->
+ mnesia:delete_object(R)
+ end, Rs);
+ _ ->
+ ok
+ end
+ end,
+ mnesia:transaction(F)
+ end.
+
unregister_routes(Domains) ->
lists:foreach(fun(Domain) ->
unregister_route(Domain)
end, Domains).
+read_route(Domain) ->
+ [{D,P,H}
+ || #route{domain=D, pid=P, local_hint=H} <- mnesia:dirty_read({route, Domain})].
dirty_get_all_routes() ->
lists:usort(
@@ -376,10 +405,16 @@ do_route(OrigFrom, OrigTo, OrigPacket) ->
case ejabberd_hooks:run_fold(filter_packet,
{OrigFrom, OrigTo, OrigPacket}, []) of
{From, To, Packet} ->
- LDomain = exmpp_jid:prep_domain(To),
- case mnesia:dirty_read(route, LDomain) of
+ LDstDomain = exmpp_jid:prep_domain_as_list(To),
+ Destination = ejabberd:normalize_host(LDstDomain),
+ case mnesia:dirty_read(route, list_to_binary(Destination)) of
[] ->
- ejabberd_s2s:route(From, To, Packet);
+ case ejabberd_global_router:find_route(Destination) of
+ no_route ->
+ ejabberd_s2s:route(From, To, Packet);
+ Route ->
+ ejabberd_global_router:route(Route, From, To, Packet)
+ end;
[R] ->
Pid = R#route.pid,
if
@@ -396,7 +431,6 @@ do_route(OrigFrom, OrigTo, OrigPacket) ->
drop
end;
Rs ->
- LDstDomain = exmpp_jid:prep_domain_as_list(To),
Value = case ejabberd_config:get_local_option(
{domain_balancing, LDstDomain}) of
undefined -> now();
diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl
index ea722ae17..f6d09276f 100644
--- a/src/ejabberd_sm.erl
+++ b/src/ejabberd_sm.erl
@@ -353,6 +353,12 @@ init([]) ->
mnesia:add_table_index(session, us),
mnesia:add_table_copy(session, node(), ram_copies),
ets:new(sm_iqtable, [named_table]),
+ ejabberd_hooks:add(roster_in_subscription, global,
+ ejabberd_sm, check_in_subscription, 20),
+ ejabberd_hooks:add(offline_message_hook, global,
+ ejabberd_sm, bounce_offline_message, 100),
+ ejabberd_hooks:add(remove_user, global,
+ ejabberd_sm, disconnect_removed_user, 100),
ejabberd_hooks:add(node_hash_update, ?MODULE, migrate, 100),
lists:foreach(
fun(Host) ->
@@ -417,19 +423,19 @@ handle_info({route, From, To, Packet}, State) ->
end,
{noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) ->
- ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function}),
+ ets:insert(sm_iqtable, {{XMLNS, ejabberd:normalize_host(Host)}, Module, Function}),
{noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) ->
- ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function, Opts}),
+ ets:insert(sm_iqtable, {{XMLNS, ejabberd:normalize_host(Host)}, Module, Function, Opts}),
{noreply, State};
handle_info({unregister_iq_handler, Host, XMLNS}, State) ->
- case ets:lookup(sm_iqtable, {XMLNS, Host}) of
+ case ets:lookup(sm_iqtable, {XMLNS, ejabberd:normalize_host(Host)}) of
[{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ ->
ok
end,
- ets:delete(sm_iqtable, {XMLNS, Host}),
+ ets:delete(sm_iqtable, {XMLNS, ejabberd:normalize_host(Host)}),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
@@ -763,7 +769,7 @@ process_iq(From, To, Packet) ->
case exmpp_iq:xmlel_to_iq(Packet) of
#iq{kind = request, ns = XMLNS} = IQ_Rec ->
LServer = exmpp_jid:prep_domain(To),
- case ets:lookup(sm_iqtable, {XMLNS, LServer}) of
+ case ets:lookup(sm_iqtable, {XMLNS, ejabberd:normalize_host(LServer)}) of
[{_, Module, Function}] ->
ResIQ = Module:Function(From, To, IQ_Rec),
if
@@ -774,11 +780,17 @@ process_iq(From, To, Packet) ->
ok
end;
[{_, Module, Function, Opts}] ->
- gen_iq_handler:handle(LServer,
- Module, Function, Opts, From, To, IQ_Rec);
+ gen_iq_handler:handle(LServer,
+ Module, Function, Opts, From, To, IQ_Rec);
[] ->
- Err = exmpp_iq:error(Packet, 'service-unavailable'),
- ejabberd_router:route(To, From, Err)
+ case ets:lookup(sm_iqtable, {XMLNS, global}) of
+ [{_, Module, Function, Opts}] ->
+ gen_iq_handler:handle(global, Module, Function, Opts,
+ From, To, IQ_Rec);
+ [] ->
+ Err = exmpp_iq:error(Packet, 'service-unavailable'),
+ ejabberd_router:route(To, From, Err)
+ end
end;
#iq{kind = response} ->
ok;
diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl
index 1fc193e5d..e088c032b 100644
--- a/src/ejabberd_sup.erl
+++ b/src/ejabberd_sup.erl
@@ -42,13 +42,13 @@ init([]) ->
brutal_kill,
worker,
[ejabberd_hooks]},
- SystemMonitor =
- {ejabberd_system_monitor,
- {ejabberd_system_monitor, start_link, []},
+ GlobalRouter =
+ {ejabberd_global_router,
+ {ejabberd_global_router, start_link, []},
permanent,
brutal_kill,
worker,
- [ejabberd_system_monitor]},
+ [ejabberd_global_router]},
Router =
{ejabberd_router,
{ejabberd_router, start_link, []},
@@ -137,6 +137,13 @@ init([]) ->
infinity,
supervisor,
[ejabberd_tmp_sup]},
+ Hosts =
+ {ejabberd_hosts,
+ {ejabberd_hosts, start_link, []},
+ permanent,
+ brutal_kill,
+ worker,
+ [ejabberd_hosts]},
HTTPSupervisor =
{ejabberd_http_sup,
{ejabberd_tmp_sup, start_link,
@@ -186,8 +193,8 @@ init([]) ->
[ejabberd_cluster]},
{ok, {{one_for_one, 10, 1},
[Hooks,
+ GlobalRouter,
Cluster,
- SystemMonitor,
Router,
Router_multicast,
SM,
@@ -199,6 +206,7 @@ init([]) ->
S2SInSupervisor,
S2SOutSupervisor,
ServiceSupervisor,
+ Hosts,
HTTPSupervisor,
HTTPPollSupervisor,
IQSupervisor,
diff --git a/src/ejabberdctl.template b/src/ejabberdctl.template
index 06681f16a..5c73bc8ce 100644
--- a/src/ejabberdctl.template
+++ b/src/ejabberdctl.template
@@ -57,8 +57,9 @@ if [ "$ERLANG_NODE_ARG" != "" ] ; then
fi
# check the proper system user is used
-ID=`id -g`
-GIDS=`id -G`
+if [ `uname -s` = "SunOS" ]; then IDCMD=/usr/xpg4/bin/id; else IDCMD=id; fi
+ID=`$IDCMD -g`
+EJID=`$IDCMD -g $INSTALLUSER`
EJID=`id -g $INSTALLUSER`
EXEC_CMD="false"
for GID in $GIDS; do
@@ -77,10 +78,13 @@ fi
NAME=-name
[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && NAME=-sname
-if [ "$FIREWALL_WINDOW" = "" ] ; then
- KERNEL_OPTS=""
-else
- KERNEL_OPTS="-kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
+KERNEL_OPTS=""
+if [ "$FIREWALL_WINDOW" != "" ] ; then
+ KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
+fi
+
+if [ "$DIST_INTERFACE" != "" ] ; then
+ KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_use_interface \"${DIST_INTERFACE}\""
fi
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS"
@@ -146,7 +150,7 @@ start ()
$KERNEL_OPTS \
-s ejabberd \
-sasl sasl_error_logger \\{file,\\\"$SASL_LOG_PATH\\\"\\} \
- $ERLANG_OPTS $ARGS \"$@\" $ERL_ARGS"
+ $ERLANG_OPTS -- $ARGS \"$@\" $ERL_ARGS"
}
# attach to server
@@ -177,7 +181,7 @@ debug ()
$NAME debug-${TTY}-${ERLANG_NODE} \
-remsh $ERLANG_NODE \
$KERNEL_OPTS \
- $ERLANG_OPTS $ARGS \"$@\" $ERL_ARGS"
+ $ERLANG_OPTS -- $ARGS \"$@\" $ERL_ARGS"
}
# start interactive server
@@ -209,7 +213,7 @@ live ()
-mnesia dir \"\\\"$SPOOLDIR\\\"\" \
$KERNEL_OPTS \
-s ejabberd \
- $ERLANG_OPTS $ARGS \"$@\" $ERL_ARGS"
+ $ERLANG_OPTS -- $ARGS \"$@\" $ERL_ARGS"
}
etop()
diff --git a/src/ejd2odbc.erl b/src/ejd2odbc.erl
index ea68e509d..a1af8f3e6 100644
--- a/src/ejd2odbc.erl
+++ b/src/ejd2odbc.erl
@@ -261,7 +261,7 @@ export_private_storage(ServerS, Output) ->
LXMLNS = ejabberd_odbc:escape(atom_to_list(XMLNS)),
SData = ejabberd_odbc:escape(
exmpp_xml:document_to_list(Data)),
- odbc_queries:set_private_data_sql(Username, LXMLNS, SData);
+ odbc_queries:set_private_data_sql(LServer, Username, LXMLNS, SData);
(_Host, _R) ->
[]
end).
diff --git a/src/gen_mod.erl b/src/gen_mod.erl
index a1b3d140b..6e7a8fed0 100644
--- a/src/gen_mod.erl
+++ b/src/gen_mod.erl
@@ -40,6 +40,7 @@
loaded_modules_with_opts/1,
get_hosts/2,
get_module_proc/2,
+ expand_host_name/3,
is_loaded/2]).
-export([behaviour_info/1]).
@@ -192,6 +193,9 @@ loaded_modules_with_opts(Host) ->
[],
[{{'$1', '$2'}}]}]).
+set_module_opts_mnesia(global, _Module, _Opts) ->
+ %% Modules on the global host are usually static, so we shouldn't manipulate them.
+ ok;
set_module_opts_mnesia(Host, Module, Opts) ->
Modules = case ejabberd_config:get_local_option({modules, Host}) of
undefined ->
@@ -203,6 +207,9 @@ set_module_opts_mnesia(Host, Module, Opts) ->
Modules2 = [{Module, Opts} | Modules1],
ejabberd_config:add_local_option({modules, Host}, Modules2).
+del_module_mnesia(global, _Module) ->
+ %% Modules on the global host are usually static, so we shouldn't manipulate them.
+ ok;
del_module_mnesia(Host, Module) ->
Modules = case ejabberd_config:get_local_option({modules, Host}) of
undefined ->
@@ -226,6 +233,8 @@ get_hosts(Opts, Prefix) ->
Hosts
end.
+get_module_proc(global, Base) ->
+ list_to_atom(atom_to_list(Base) ++ "__global");
get_module_proc(Host, {frontend, Base}) ->
get_module_proc("frontend_" ++ Host, Base);
get_module_proc(Host, Base) ->
@@ -234,3 +243,11 @@ get_module_proc(Host, Base) ->
is_loaded(Host, Module) ->
ets:member(ejabberd_modules, {Module, Host}).
+expand_host_name(Host, Opts, DefaultPrefix) ->
+ case Host of
+ global ->
+ {global, gen_mod:get_opt(prefix, Opts, DefaultPrefix)};
+ _ ->
+ gen_mod:get_opt_host(Host, Opts, DefaultPrefix ++ ".@HOST@")
+ end.
+
diff --git a/src/odbc/ejabberd_odbc.erl b/src/odbc/ejabberd_odbc.erl
index 79ab48d14..53895cc70 100644
--- a/src/odbc/ejabberd_odbc.erl
+++ b/src/odbc/ejabberd_odbc.erl
@@ -33,6 +33,7 @@
%% External exports
-export([start/1, start_link/2,
+ running/1,
sql_query/2,
sql_query_t/1,
sql_transaction/2,
@@ -106,6 +107,14 @@ sql_query_on_all_connections(Host, Query) ->
erlang:now()}, ?TRANSACTION_TIMEOUT) end,
lists:map(F, ejabberd_odbc_sup:get_pids(Host)).
+%% Predicate returning true if there is an odbc process running for
+%% host Host, false otherwise.
+running(Host) ->
+ case catch ejabberd_odbc_sup:get_random_pid(Host) of
+ P when is_pid(P) -> true;
+ {'EXIT', {noproc, _}} -> false
+ end.
+
%% SQL transaction based on a list of queries
%% This function automatically
sql_transaction(Host, Queries) when is_list(Queries) ->
@@ -497,7 +506,11 @@ pgsql_to_odbc({ok, PGSQLResult}) ->
pgsql_item_to_odbc({"SELECT", Rows, Recs}) ->
{selected,
- [element(1, Row) || Row <- Rows],
+ [case Row of
+ {desc, _, Col, _, _, _, _, _} -> Col; % Recent pgsql driver API change.
+ _ -> element(1, Row)
+ end
+ || Row <- Rows],
[list_to_tuple(Rec) || Rec <- Recs]};
pgsql_item_to_odbc("INSERT " ++ OIDN) ->
[_OID, N] = string:tokens(OIDN, " "),
@@ -555,6 +568,7 @@ log(Level, Format, Args) ->
?ERROR_MSG(Format, Args)
end.
+%% TODO: update this function to handle the case clase {host, VhostName}
db_opts(Host) ->
case ejabberd_config:get_local_option({odbc_server, Host}) of
%% Default pgsql port
diff --git a/src/odbc/ejabberd_odbc_sup.erl b/src/odbc/ejabberd_odbc_sup.erl
index 45ede1835..819028d44 100644
--- a/src/odbc/ejabberd_odbc_sup.erl
+++ b/src/odbc/ejabberd_odbc_sup.erl
@@ -100,11 +100,16 @@ init([Host]) ->
end, lists:seq(1, PoolSize))}}.
get_pids(Host) ->
- Rs = mnesia:dirty_read(sql_pool, Host),
- [R#sql_pool.pid || R <- Rs].
+ case ejabberd_config:get_local_option({odbc_server, Host}) of
+ {host, Host1} ->
+ get_pids(Host1);
+ _ ->
+ Rs = mnesia:dirty_read(sql_pool, Host),
+ [R#sql_pool.pid || R <- Rs]
+ end.
get_random_pid(Host) ->
- Pids = get_pids(Host),
+ Pids = get_pids(ejabberd:normalize_host(Host)),
lists:nth(erlang:phash(now(), length(Pids)), Pids).
add_pid(Host, Pid) ->
diff --git a/src/odbc/mysql.sql b/src/odbc/mysql.sql
index 9d8fa9bb9..29a71ce24 100644
--- a/src/odbc/mysql.sql
+++ b/src/odbc/mysql.sql
@@ -20,21 +20,35 @@
-- Needs MySQL (at least 4.0.x) with innodb back-end
SET table_type=InnoDB;
+CREATE TABLE hosts (
+ clusterid integer NOT NULL,
+ host varchar(250) NOT NULL PRIMARY KEY,
+ config text NOT NULL
+) CHARACTER SET utf8;
+
+INSERT INTO hosts (clusterid, host, config)
+VALUES (1, 'localhost', '');
+
CREATE TABLE users (
- username varchar(250) PRIMARY KEY,
+ host varchar(250) NOT NULL,
+ username varchar(250) NOT NULL,
password text NOT NULL,
- created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
+ created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (host, username)
) CHARACTER SET utf8;
CREATE TABLE last (
- username varchar(250) PRIMARY KEY,
+ host varchar(250) NOT NULL,
+ username varchar(250) NOT NULL,
seconds text NOT NULL,
- state text NOT NULl
+ state text NOT NULL,
+ PRIMARY KEY (host, username)
) CHARACTER SET utf8;
CREATE TABLE rosterusers (
+ host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
jid varchar(250) NOT NULL,
nick text NOT NULL,
@@ -44,42 +58,44 @@ CREATE TABLE rosterusers (
server character(1) NOT NULL,
subscribe text NOT NULL,
type text,
- created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
+ created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (host(75), username(75), jid(75))
) CHARACTER SET utf8;
-CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers(username(75), jid(75));
CREATE INDEX i_rosteru_username ON rosterusers(username);
CREATE INDEX i_rosteru_jid ON rosterusers(jid);
CREATE TABLE rostergroups (
+ host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
jid varchar(250) NOT NULL,
- grp text NOT NULL
+ grp text NOT NULL,
+ PRIMARY KEY (host(75), username(75), jid(75))
) CHARACTER SET utf8;
-CREATE INDEX pk_rosterg_user_jid ON rostergroups(username(75), jid(75));
-
-
CREATE TABLE spool (
+ host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
xml text NOT NULL,
seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
- created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
+ created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (host, username, seq)
) CHARACTER SET utf8;
-CREATE INDEX i_despool USING BTREE ON spool(username);
-
CREATE TABLE vcard (
- username varchar(250) PRIMARY KEY,
+ host varchar(250) NOT NULL,
+ username varchar(250) NOT NULL,
vcard text NOT NULL,
- created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
+ created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (host, username)
) CHARACTER SET utf8;
CREATE TABLE vcard_search (
+ host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
- lusername varchar(250) PRIMARY KEY,
+ lusername varchar(250) NOT NULL,
fn text NOT NULL,
lfn varchar(250) NOT NULL,
family text NOT NULL,
@@ -101,7 +117,8 @@ CREATE TABLE vcard_search (
orgname text NOT NULL,
lorgname varchar(250) NOT NULL,
orgunit text NOT NULL,
- lorgunit varchar(250) NOT NULL
+ lorgunit varchar(250) NOT NULL,
+ PRIMARY KEY (host, lusername)
) CHARACTER SET utf8;
CREATE INDEX i_vcard_search_lfn ON vcard_search(lfn);
@@ -117,20 +134,21 @@ CREATE INDEX i_vcard_search_lorgname ON vcard_search(lorgname);
CREATE INDEX i_vcard_search_lorgunit ON vcard_search(lorgunit);
CREATE TABLE privacy_default_list (
- username varchar(250) PRIMARY KEY,
- name varchar(250) NOT NULL
+ host varchar(250) NOT NULL,
+ username varchar(250),
+ name varchar(250) NOT NULL,
+ PRIMARY KEY (host, username)
) CHARACTER SET utf8;
CREATE TABLE privacy_list (
+ host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
name varchar(250) NOT NULL,
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
- created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
+ created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (host, username, name)
) CHARACTER SET utf8;
-CREATE INDEX i_privacy_list_username USING BTREE ON privacy_list(username);
-CREATE UNIQUE INDEX i_privacy_list_username_name USING BTREE ON privacy_list (username(75), name(75));
-
CREATE TABLE privacy_list_data (
id bigint,
t character(1) NOT NULL,
@@ -145,14 +163,15 @@ CREATE TABLE privacy_list_data (
) CHARACTER SET utf8;
CREATE TABLE private_storage (
+ host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
namespace varchar(250) NOT NULL,
data text NOT NULL,
- created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
+ created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (host(75), username(75), namespace(75))
) CHARACTER SET utf8;
CREATE INDEX i_private_storage_username USING BTREE ON private_storage(username);
-CREATE UNIQUE INDEX i_private_storage_username_namespace USING BTREE ON private_storage(username(75), namespace(75));
-- Not tested in mysql
CREATE TABLE roster_version (
diff --git a/src/odbc/odbc_queries.erl b/src/odbc/odbc_queries.erl
index 14a89dae1..77409d048 100644
--- a/src/odbc/odbc_queries.erl
+++ b/src/odbc/odbc_queries.erl
@@ -41,7 +41,7 @@
list_users/2,
users_number/1,
users_number/2,
- add_spool_sql/2,
+ add_spool_sql/3,
add_spool/2,
get_and_del_spool_msg_t/2,
del_spool_msg/2,
@@ -52,27 +52,27 @@
get_roster_by_jid/3,
get_rostergroup_by_jid/3,
del_roster/3,
- del_roster_sql/2,
+ del_roster_sql/3,
update_roster/5,
- update_roster_sql/4,
+ update_roster_sql/5,
roster_subscribe/4,
get_subscription/3,
set_private_data/4,
- set_private_data_sql/3,
+ set_private_data_sql/4,
get_private_data/3,
del_user_private_storage/2,
get_default_privacy_list/2,
- get_default_privacy_list_t/1,
+ get_default_privacy_list_t/2,
get_privacy_list_names/2,
- get_privacy_list_names_t/1,
+ get_privacy_list_names_t/2,
get_privacy_list_id/3,
- get_privacy_list_id_t/2,
+ get_privacy_list_id_t/3,
get_privacy_list_data/3,
get_privacy_list_data_by_id/2,
- set_default_privacy_list/2,
+ set_default_privacy_list/3,
unset_default_privacy_list/2,
- remove_privacy_list/2,
- add_privacy_list/2,
+ remove_privacy_list/3,
+ add_privacy_list/3,
set_privacy_list/2,
del_privacy_lists/3,
set_vcard/26,
@@ -122,65 +122,75 @@ sql_transaction(LServer, F) ->
ejabberd_odbc:sql_transaction(LServer, F).
get_last(LServer, Username) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query(
LServer,
["select seconds, state from last "
- "where username='", Username, "'"]).
+ "where username='", Username, "' and host='", Host, "'"]).
set_last_t(LServer, Username, Seconds, State) ->
+ Host = escape(LServer),
%% MREMOND: I think this should be turn into a non transactional behaviour
ejabberd_odbc:sql_transaction(
LServer,
fun() ->
- update_t("last", ["username", "seconds", "state"],
- [Username, Seconds, State],
- ["username='", Username, "'"])
+ update_t("last", ["username", "host", "seconds", "state"],
+ [Username, Host, Seconds, State],
+ ["username='", Username, "' and host='", Host, "'"])
end).
del_last(LServer, Username) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query(
LServer,
- ["delete from last where username='", Username, "'"]).
+ ["delete from last where username='", Username, "' and host='", Host, "'"]).
get_password(LServer, Username) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query(
LServer,
["select password from users "
- "where username='", Username, "';"]).
+ "where username='", Username, "' and host='", Host, "';"]).
set_password_t(LServer, Username, Pass) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_transaction(
LServer,
fun() ->
- update_t("users", ["username", "password"],
- [Username, Pass],
- ["username='", Username ,"'"])
+ update_t("users", ["username", "host", "password"],
+ [Username, Host, Pass],
+ ["username='", Username ,"' and host='", Host, "'"])
end).
add_user(LServer, Username, Pass) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query(
LServer,
- ["insert into users(username, password) "
- "values ('", Username, "', '", Pass, "');"]).
+ ["insert into users(username, host, password) "
+ "values ('", Username, "', '", Host, "', '", Pass, "');"]).
del_user(LServer, Username) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query(
LServer,
- ["delete from users where username='", Username ,"';"]).
+ ["delete from users where username='", Username ,"' and host='", Host, "';"]).
-del_user_return_password(_LServer, Username, Pass) ->
- P = ejabberd_odbc:sql_query_t(
- ["select password from users where username='",
- Username, "';"]),
+del_user_return_password(LServer, Username, Pass) ->
+ Host = escape(LServer),
+ ejabberd_odbc:sql_query_t(
+ ["select password from users where username='",
+ Username, "' and host='", Host, "';"]),
ejabberd_odbc:sql_query_t(["delete from users "
"where username='", Username,
+ "' and host='", Host,
"' and password='", Pass, "';"]),
- P.
+ Pass. % REVIEWME
list_users(LServer) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query(
LServer,
- "select username from users").
+ ["select username from users where host='", Host, "'"]).
list_users(LServer, [{from, Start}, {to, End}]) when is_integer(Start) and
is_integer(End) ->
@@ -211,6 +221,7 @@ list_users(LServer, [{prefix, Prefix},
"limit ~w offset ~w ", [Prefix, Limit, Offset])).
users_number(LServer) ->
+ Host = escape(LServer),
case element(1, ejabberd_config:get_local_option({odbc_server, LServer})) of
mysql ->
ejabberd_odbc:sql_query(
@@ -224,13 +235,13 @@ users_number(LServer) ->
"select reltuples from pg_class where oid = 'users'::regclass::oid");
_ ->
ejabberd_odbc:sql_query(
- LServer,
- "select count(*) from users")
+ LServer,
+ ["select count(*) from users where host='", Host, "'"])
end;
_ ->
ejabberd_odbc:sql_query(
- LServer,
- "select count(*) from users")
+ LServer,
+ ["select count(*) from users where host='", Host, "'"])
end.
users_number(LServer, [{prefix, Prefix}]) when is_list(Prefix) ->
@@ -243,10 +254,10 @@ users_number(LServer, [{prefix, Prefix}]) when is_list(Prefix) ->
users_number(LServer, []) ->
users_number(LServer).
-
-add_spool_sql(Username, XML) ->
- ["insert into spool(username, xml) "
- "values ('", Username, "', '",
+add_spool_sql(LServer, Username, XML) ->
+ Host = escape(LServer),
+ ["insert into spool(username, host, xml) "
+ "values ('", Username, "', '", Host, "', '",
XML,
"');"].
@@ -255,158 +266,177 @@ add_spool(LServer, Queries) ->
LServer, Queries).
get_and_del_spool_msg_t(LServer, Username) ->
+ Host = escape(LServer),
F = fun() ->
Result = ejabberd_odbc:sql_query_t(
["select username, xml from spool where username='", Username, "'"
+ " and host='", Host, "'"
" order by seq;"]),
ejabberd_odbc:sql_query_t(
- ["delete from spool where username='", Username, "';"]),
+ ["delete from spool where username='", Username, "' and host='", Host, "';"]),
Result
end,
ejabberd_odbc:sql_transaction(LServer,F).
del_spool_msg(LServer, Username) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query(
LServer,
- ["delete from spool where username='", Username, "';"]).
+ ["delete from spool where username='", Username, "' and host='", Host, "';"]).
get_roster(LServer, Username) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query(
LServer,
["select username, jid, nick, subscription, ask, "
"askmessage, server, subscribe, type from rosterusers "
- "where username='", Username, "'"]).
+ "where username='", Username, "' and host='", Host, "'"]).
get_roster_jid_groups(LServer, Username) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query(
LServer,
["select jid, grp from rostergroups "
- "where username='", Username, "'"]).
+ "where username='", Username, "' and host='", Host, "'"]).
-get_roster_groups(_LServer, Username, SJID) ->
+get_roster_groups(LServer, Username, SJID) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query_t(
["select grp from rostergroups "
- "where username='", Username, "' "
+ "where username='", Username, "' and host='", Host, "' "
"and jid='", SJID, "';"]).
del_user_roster_t(LServer, Username) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_transaction(
LServer,
fun() ->
ejabberd_odbc:sql_query_t(
["delete from rosterusers "
- " where username='", Username, "';"]),
+ " where username='", Username, "' and host='", Host, "';"]),
ejabberd_odbc:sql_query_t(
["delete from rostergroups "
- " where username='", Username, "';"])
+ " where username='", Username, "' and host='", Host, "';"])
end).
-get_roster_by_jid(_LServer, Username, SJID) ->
+get_roster_by_jid(LServer, Username, SJID) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query_t(
["select username, jid, nick, subscription, "
"ask, askmessage, server, subscribe, type from rosterusers "
- "where username='", Username, "' "
+ "where username='", Username, "' and host='", Host, "' "
"and jid='", SJID, "';"]).
get_rostergroup_by_jid(LServer, Username, SJID) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query(
LServer,
["select grp from rostergroups "
- "where username='", Username, "' "
+ "where username='", Username, "' and host='", Host, "' "
"and jid='", SJID, "'"]).
-del_roster(_LServer, Username, SJID) ->
+del_roster(LServer, Username, SJID) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query_t(
["delete from rosterusers "
- " where username='", Username, "' "
+ " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"]),
ejabberd_odbc:sql_query_t(
["delete from rostergroups "
- " where username='", Username, "' "
+ " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"]).
-del_roster_sql(Username, SJID) ->
+del_roster_sql(LServer, Username, SJID) ->
+ Host = escape(LServer),
[["delete from rosterusers "
- " where username='", Username, "' "
+ " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"],
["delete from rostergroups "
- " where username='", Username, "' "
+ " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"]].
-update_roster(_LServer, Username, SJID, ItemVals, ItemGroups) ->
+update_roster(LServer, Username, SJID, ItemVals, ItemGroups) ->
+ Host = escape(LServer),
update_t("rosterusers",
- ["username", "jid", "nick", "subscription", "ask",
+ ["host", "username", "jid", "nick", "subscription", "ask",
"askmessage", "server", "subscribe", "type"],
- ItemVals,
- ["username='", Username, "' and jid='", SJID, "'"]),
+ [Host | ItemVals],
+ ["username='", Username, "' and host='", Host, "' and jid='", SJID, "'"]),
ejabberd_odbc:sql_query_t(
["delete from rostergroups "
- " where username='", Username, "' "
+ " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"]),
lists:foreach(fun(ItemGroup) ->
ejabberd_odbc:sql_query_t(
["insert into rostergroups("
- " username, jid, grp) "
- " values ('", string:join(ItemGroup, "', '"), "');"])
+ " host, username, jid, grp) "
+ " values ('", Host, "', '", string:join(ItemGroup, "', '"), "');"])
end,
ItemGroups).
-update_roster_sql(Username, SJID, ItemVals, ItemGroups) ->
+update_roster_sql(LServer, Username, SJID, ItemVals, ItemGroups) ->
+ Host = escape(LServer),
[["delete from rosterusers "
- " where username='", Username, "' "
+ " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"],
["insert into rosterusers("
- " username, jid, nick, "
+ " host, username, jid, nick, "
" subscription, ask, askmessage, "
" server, subscribe, type) "
- " values ('", string:join(ItemVals, "', '"), "');"],
+ " values ('", Host, "', '", string:join(ItemVals, "', '"), "');"],
["delete from rostergroups "
- " where username='", Username, "' "
+ " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"]] ++
[["insert into rostergroups("
- " username, jid, grp) "
- " values ('", string:join(ItemGroup, "', '"), "');"] ||
+ " host, username, jid, grp) "
+ " values ('", Host, "', '", string:join(ItemGroup, "', '"), "');"] ||
ItemGroup <- ItemGroups].
-roster_subscribe(_LServer, Username, SJID, ItemVals) ->
+roster_subscribe(LServer, Username, SJID, ItemVals) ->
+ Host = escape(LServer),
update_t("rosterusers",
- ["username", "jid", "nick", "subscription", "ask",
+ ["host", "username", "jid", "nick", "subscription", "ask",
"askmessage", "server", "subscribe", "type"],
- ItemVals,
- ["username='", Username, "' and jid='", SJID, "'"]).
+ [Host | ItemVals],
+ ["username='", Username, "' and host='", Host, "' and jid='", SJID, "'"]).
get_subscription(LServer, Username, SJID) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query(
LServer,
["select subscription from rosterusers "
- "where username='", Username, "' "
+ "where username='", Username, "' and host='", Host, "' "
"and jid='", SJID, "'"]).
-set_private_data(_LServer, Username, LXMLNS, SData) ->
+set_private_data(LServer, Username, LXMLNS, SData) ->
+ Host = escape(LServer),
update_t("private_storage",
- ["username", "namespace", "data"],
- [Username, LXMLNS, SData],
- ["username='", Username, "' and namespace='", LXMLNS, "'"]).
+ ["host", "username", "namespace", "data"],
+ [Host, Username, LXMLNS, SData],
+ ["username='", Username, "' and host='", Host, "' and namespace='", LXMLNS, "'"]).
-set_private_data_sql(Username, LXMLNS, SData) ->
+set_private_data_sql(LServer, Username, LXMLNS, SData) ->
+ Host = escape(LServer),
[["delete from private_storage "
- "where username='", Username, "' and "
+ "where username='", Username, "' and host='", Host, "' and "
"namespace='", LXMLNS, "';"],
- ["insert into private_storage(username, namespace, data) "
- "values ('", Username, "', '", LXMLNS, "', "
+ ["insert into private_storage(host, username, namespace, data) "
+ "values ('", Host, "', '", Username, "', '", LXMLNS, "', "
"'", SData, "');"]].
get_private_data(LServer, Username, LXMLNS) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query(
LServer,
["select data from private_storage "
- "where username='", Username, "' and "
+ "where username='", Username, "' and host='", Host, "' and "
"namespace='", LXMLNS, "';"]).
del_user_private_storage(LServer, Username) ->
+ Host = escape(LServer),
ejabberd_odbc:sql_query(
LServer,
- ["delete from private_storage where username='", Username, "';"]).
+ ["delete from private_storage where username='", Username, "' and host='", Host, "';"]).
set_vcard(LServer, LUsername, SBDay, SCTRY, SEMail, SFN, SFamily, SGiven,
SLBDay, SLCTRY, SLEMail, SLFN, SLFamily, SLGiven, SLLocality,
@@ -415,16 +445,16 @@ set_vcard(LServer, LUsername, SBDay, SCTRY, SEMail, SFN, SFamily, SGiven,
ejabberd_odbc:sql_transaction(
LServer,
fun() ->
- update_t("vcard", ["username", "vcard"],
- [LUsername, SVCARD],
- ["username='", LUsername, "'"]),
+ update_t("vcard", ["host", "username", "vcard"],
+ [LServer, LUsername, SVCARD],
+ ["username='", LUsername, "' and host='", LServer, "'"]),
update_t("vcard_search",
- ["username", "lusername", "fn", "lfn", "family",
+ ["host", "username", "lusername", "fn", "lfn", "family",
"lfamily", "given", "lgiven", "middle", "lmiddle",
"nickname", "lnickname", "bday", "lbday", "ctry",
"lctry", "locality", "llocality", "email", "lemail",
"orgname", "lorgname", "orgunit", "lorgunit"],
- [Username, LUsername, SFN, SLFN, SFamily, SLFamily,
+ [LServer, Username, LUsername, SFN, SLFN, SFamily, SLFamily,
SGiven, SLGiven, SMiddle, SLMiddle, SNickname,
SLNickname, SBDay, SLBDay, SCTRY, SLCTRY,
SLocality, SLLocality, SEMail, SLEMail, SOrgName,
@@ -436,7 +466,7 @@ get_vcard(LServer, Username) ->
ejabberd_odbc:sql_query(
LServer,
["select vcard from vcard "
- "where username='", Username, "';"]).
+ "where username='", Username, "' and host='", LServer, "';"]).
del_vcard(LServer, Username) ->
ejabberd_odbc:sql_transaction(
@@ -457,34 +487,34 @@ get_default_privacy_list(LServer, Username) ->
ejabberd_odbc:sql_query(
LServer,
["select name from privacy_default_list "
- "where username='", Username, "';"]).
+ "where username='", Username, "' and host='", LServer, "';"]).
-get_default_privacy_list_t(Username) ->
+get_default_privacy_list_t(LServer, Username) ->
ejabberd_odbc:sql_query_t(
["select name from privacy_default_list "
- "where username='", Username, "';"]).
+ "where username='", Username, "' and host='", LServer, "';"]).
get_privacy_list_names(LServer, Username) ->
ejabberd_odbc:sql_query(
LServer,
["select name from privacy_list "
- "where username='", Username, "';"]).
+ "where username='", Username, "' and host='", LServer, "';"]).
-get_privacy_list_names_t(Username) ->
+get_privacy_list_names_t(LServer, Username) ->
ejabberd_odbc:sql_query_t(
["select name from privacy_list "
- "where username='", Username, "';"]).
+ "where username='", Username, "' and host='", LServer, "';"]).
get_privacy_list_id(LServer, Username, SName) ->
ejabberd_odbc:sql_query(
LServer,
["select id from privacy_list "
- "where username='", Username, "' and name='", SName, "';"]).
+ "where username='", Username, "' and name='", SName, "' and host='", LServer, "';"]).
-get_privacy_list_id_t(Username, SName) ->
+get_privacy_list_id_t(LServer, Username, SName) ->
ejabberd_odbc:sql_query_t(
["select id from privacy_list "
- "where username='", Username, "' and name='", SName, "';"]).
+ "where username='", Username, "' and name='", SName, "' and host='", LServer, "';"]).
get_privacy_list_data(LServer, Username, SName) ->
ejabberd_odbc:sql_query(
@@ -493,7 +523,7 @@ get_privacy_list_data(LServer, Username, SName) ->
"match_message, match_presence_in, match_presence_out "
"from privacy_list_data "
"where id = (select id from privacy_list where "
- " username='", Username, "' and name='", SName, "') "
+ " username='", Username, "' and name='", SName, "' and host='", LServer, "') "
"order by ord;"]).
get_privacy_list_data_by_id(LServer, ID) ->
@@ -504,25 +534,26 @@ get_privacy_list_data_by_id(LServer, ID) ->
"from privacy_list_data "
"where id='", ID, "' order by ord;"]).
-set_default_privacy_list(Username, SName) ->
- update_t("privacy_default_list", ["username", "name"],
- [Username, SName], ["username='", Username, "'"]).
+set_default_privacy_list(LServer, Username, SName) ->
+ update_t("privacy_default_list", ["host", "username", "name"],
+ [LServer, Username, SName],
+ ["host='", LServer, "' and username='", Username, "'"]).
unset_default_privacy_list(LServer, Username) ->
ejabberd_odbc:sql_query(
LServer,
["delete from privacy_default_list "
- " where username='", Username, "';"]).
+ " where username='", Username, "' and host='", LServer, "';"]).
-remove_privacy_list(Username, SName) ->
+remove_privacy_list(LServer, Username, SName) ->
ejabberd_odbc:sql_query_t(
["delete from privacy_list "
- "where username='", Username, "' and name='", SName, "';"]).
+ "where username='", Username, "' and name='", SName, "' and host='", LServer, "';"]).
-add_privacy_list(Username, SName) ->
+add_privacy_list(LServer, Username, SName) ->
ejabberd_odbc:sql_query_t(
- ["insert into privacy_list(username, name) "
- "values ('", Username, "', '", SName, "');"]).
+ ["insert into privacy_list(host, username, name) "
+ "values ('", LServer, "', '", Username, "', '", SName, "');"]).
set_privacy_list(ID, RItems) ->
ejabberd_odbc:sql_query_t(
@@ -542,13 +573,13 @@ set_privacy_list(ID, RItems) ->
del_privacy_lists(LServer, Server, Username) ->
ejabberd_odbc:sql_query(
LServer,
- ["delete from privacy_list where username='", Username, "';"]),
+ ["delete from privacy_list where username='", Username, "' and host='", LServer, "';"]),
ejabberd_odbc:sql_query(
LServer,
["delete from privacy_list_data where value='", Username++"@"++Server, "';"]),
ejabberd_odbc:sql_query(
LServer,
- ["delete from privacy_default_list where username='", Username, "';"]).
+ ["delete from privacy_default_list where username='", Username, "' and host='", LServer, "';"]).
%% Characters to escape
escape($\0) -> "\\0";
diff --git a/src/odbc/pg.sql b/src/odbc/pg.sql
index 1d6a458aa..25bd8d9ea 100644
--- a/src/odbc/pg.sql
+++ b/src/odbc/pg.sql
@@ -18,21 +18,24 @@
--
CREATE TABLE users (
- username text PRIMARY KEY,
+ username text NOT NULL,
+ host text NOT NULL,
"password" text NOT NULL,
+ PRIMARY KEY (host, username)
created_at TIMESTAMP NOT NULL DEFAULT now()
);
-
CREATE TABLE last (
- username text PRIMARY KEY,
+ username text NOT NULL,
+ host text NOT NULL,
seconds text NOT NULL,
- state text NOT NULL
+ state text NOT NULL,
+ PRIMARY KEY (host, username)
);
-
CREATE TABLE rosterusers (
username text NOT NULL,
+ host text NOT NULL,
jid text NOT NULL,
nick text NOT NULL,
subscription character(1) NOT NULL,
@@ -41,42 +44,50 @@ CREATE TABLE rosterusers (
server character(1) NOT NULL,
subscribe text,
"type" text,
+<<<<<<< HEAD
+ PRIMARY KEY (host, username, jid)
+=======
created_at TIMESTAMP NOT NULL DEFAULT now()
+>>>>>>> 30
);
-CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers USING btree (username, jid);
-CREATE INDEX i_rosteru_username ON rosterusers USING btree (username);
-CREATE INDEX i_rosteru_jid ON rosterusers USING btree (jid);
-
-
CREATE TABLE rostergroups (
username text NOT NULL,
+ host text NOT NULL,
jid text NOT NULL,
- grp text NOT NULL
+ grp text NOT NULL,
+ PRIMARY KEY (host, username, jid)
);
-CREATE INDEX pk_rosterg_user_jid ON rostergroups USING btree (username, jid);
-
-
CREATE TABLE spool (
username text NOT NULL,
+ host text NOT NULL,
xml text NOT NULL,
seq SERIAL,
+<<<<<<< HEAD
+ PRIMARY KEY (host, username, seq)
+=======
created_at TIMESTAMP NOT NULL DEFAULT now()
+>>>>>>> 30
);
-CREATE INDEX i_despool ON spool USING btree (username);
-
-
CREATE TABLE vcard (
+<<<<<<< HEAD
+ username text NOT NULL,
+ host text NOT NULL,
+ vcard text NOT NULL,
+ PRIMARY KEY (host, username)
+=======
username text PRIMARY KEY,
vcard text NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT now()
+>>>>>>> 30
);
CREATE TABLE vcard_search (
+ host text NOT NULL,
username text NOT NULL,
- lusername text PRIMARY KEY,
+ lusername text NOT NULL,
fn text NOT NULL,
lfn text NOT NULL,
family text NOT NULL,
@@ -98,9 +109,11 @@ CREATE TABLE vcard_search (
orgname text NOT NULL,
lorgname text NOT NULL,
orgunit text NOT NULL,
- lorgunit text NOT NULL
+ lorgunit text NOT NULL,
+ PRIMARY KEY (host, lusername)
);
+CREATE INDEX i_vcard_search_lusername ON vcard_search(lusername);
CREATE INDEX i_vcard_search_lfn ON vcard_search(lfn);
CREATE INDEX i_vcard_search_lfamily ON vcard_search(lfamily);
CREATE INDEX i_vcard_search_lgiven ON vcard_search(lgiven);
@@ -114,20 +127,24 @@ CREATE INDEX i_vcard_search_lorgname ON vcard_search(lorgname);
CREATE INDEX i_vcard_search_lorgunit ON vcard_search(lorgunit);
CREATE TABLE privacy_default_list (
- username text PRIMARY KEY,
- name text NOT NULL
+ username text NOT NULL,
+ host text NOT NULL,
+ name text NOT NULL,
+ PRIMARY KEY (host, username)
);
CREATE TABLE privacy_list (
username text NOT NULL,
+ host text NOT NULL,
name text NOT NULL,
id SERIAL UNIQUE,
+<<<<<<< HEAD
+ PRIMARY KEY (host, username, name)
+=======
created_at TIMESTAMP NOT NULL DEFAULT now()
+>>>>>>> 30
);
-CREATE INDEX i_privacy_list_username ON privacy_list USING btree (username);
-CREATE UNIQUE INDEX i_privacy_list_username_name ON privacy_list USING btree (username, name);
-
CREATE TABLE privacy_list_data (
id bigint REFERENCES privacy_list(id) ON DELETE CASCADE,
t character(1) NOT NULL,
@@ -138,18 +155,28 @@ CREATE TABLE privacy_list_data (
match_iq boolean NOT NULL,
match_message boolean NOT NULL,
match_presence_in boolean NOT NULL,
- match_presence_out boolean NOT NULL
+ match_presence_out boolean NOT NULL,
+ PRIMARY KEY (id)
);
CREATE TABLE private_storage (
username text NOT NULL,
+ host text NOT NULL,
namespace text NOT NULL,
data text NOT NULL,
+<<<<<<< HEAD
+ PRIMARY KEY (host, username, namespace)
+=======
created_at TIMESTAMP NOT NULL DEFAULT now()
+>>>>>>> 30
);
-CREATE INDEX i_private_storage_username ON private_storage USING btree (username);
-CREATE UNIQUE INDEX i_private_storage_username_namespace ON private_storage USING btree (username, namespace);
+CREATE TABLE hosts (
+ clusterid integer NOT NULL,
+ host text NOT NULL,
+ config text NOT NULL,
+ PRIMARY KEY (host)
+);
CREATE TABLE roster_version (
diff --git a/src/randoms.erl b/src/randoms.erl
index 455ea3529..1b85c33e0 100644
--- a/src/randoms.erl
+++ b/src/randoms.erl
@@ -33,7 +33,12 @@
start() ->
- register(random_generator, spawn(randoms, init, [])).
+ case erlang:whereis(random_generator) of
+ Pid when is_pid(Pid) ->
+ true;
+ undefined ->
+ register(random_generator, spawn(randoms, init, []))
+ end.
init() ->
{A1, A2, A3} = now(),
diff --git a/src/web/ejabberd_web_admin.erl b/src/web/ejabberd_web_admin.erl
index ec69ac503..7d1063136 100644
--- a/src/web/ejabberd_web_admin.erl
+++ b/src/web/ejabberd_web_admin.erl
@@ -165,7 +165,7 @@ process(["doc", LocalFile], _Request) ->
process(["server", SHost | RPath] = Path, #request{auth = Auth, lang = Lang, host = HostHTTP, method = Method} = Request) ->
Host = exmpp_stringprep:nameprep(SHost),
- case lists:member(Host, ?MYHOSTS) of
+ case ?IS_MY_HOST(Host) of
true ->
case get_auth_admin(Auth, HostHTTP, Path, Method) of
{ok, {User, Server}} ->