Patch core for MH (thanks to Alexey Shchepin and Geoff Cant)

This commit is contained in:
Badlop 2010-07-22 18:41:53 +02:00
parent 9336356efd
commit cdb379a22c
28 changed files with 858 additions and 368 deletions

View File

@ -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)).

View File

@ -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

View File

@ -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)).

View File

@ -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,

View File

@ -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

View File

@ -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)).

View File

@ -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

View File

@ -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()

View File

@ -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,

View File

@ -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) ->

View File

@ -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).

View File

@ -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}).

View File

@ -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

View File

@ -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) ->

View File

@ -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).

View File

@ -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();

View File

@ -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;

View File

@ -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,

View File

@ -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()

View File

@ -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).

View File

@ -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.

View File

@ -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

View File

@ -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) ->

View File

@ -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 (

View File

@ -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";

View File

@ -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 (

View File

@ -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(),

View File

@ -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}} ->