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/3,
add_list/3, add_list/3,
match_rule/3, match_rule/3,
for_host/1,
% for debugging only % for debugging only
match_acl/3]). match_acl/3]).
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
%% @type aclspec() = all | JID_Exact | JID_Regexp | JID_Glob | Shared_Group %% @type aclspec() = all | JID_Exact | JID_Regexp | JID_Glob | Shared_Group
%% JID_Exact = {user, U} | {user, U, S} | {server, S} | {resource, R} %% 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), User = exmpp_jid:prep_node_as_list(JID),
Server = exmpp_jid:prep_domain_as_list(JID), Server = exmpp_jid:prep_domain_as_list(JID),
Resource = exmpp_jid:prep_resource_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 case Spec of
all -> all ->
true; true;
@ -243,17 +245,21 @@ match_acl(ACLName, JID, Host) ->
andalso andalso
((Host == Server) orelse ((Host == Server) orelse
((Host == global) andalso ((Host == global) andalso
lists:member(Server, ?MYHOSTS))); ?IS_MY_HOST(Server)));
{user, U, S} -> {user, U, S} ->
(U == User) andalso (S == Server); (U == User) andalso (S == Server);
{server, S} -> {server, S} ->
S == Server; S == Server;
{resource, R} -> {resource, R} ->
R == Resource; 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} -> {user_regexp, UR} ->
((Host == Server) orelse ((Host == Server) orelse
((Host == global) andalso ((Host == global) andalso
lists:member(Server, ?MYHOSTS))) ?IS_MY_HOST(Server)))
andalso is_regexp_match(User, UR); andalso is_regexp_match(User, UR);
{shared_group, G} -> {shared_group, G} ->
mod_shared_roster:is_user_in_group({User, Server}, G, Host); mod_shared_roster:is_user_in_group({User, Server}, G, Host);
@ -272,7 +278,7 @@ match_acl(ACLName, JID, Host) ->
{user_glob, UR} -> {user_glob, UR} ->
((Host == Server) orelse ((Host == Server) orelse
((Host == global) andalso ((Host == global) andalso
lists:member(Server, ?MYHOSTS))) ?IS_MY_HOST(Server)))
andalso andalso
is_glob_match(User, UR); is_glob_match(User, UR);
{user_glob, UR, S} -> {user_glob, UR, S} ->
@ -325,3 +331,9 @@ is_glob_match(String, Glob) ->
is_regexp_match(String, xmerl_regexp:sh_to_awk(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'). -author('alexey@process-one.net').
-export([start/0, stop/0, -export([start/0, stop/0,
get_pid_file/0, get_so_path/0,
get_so_path/0, get_bin_path/0]). get_bin_path/0,
get_pid_file/0,
is_my_host/1,
normalize_host/1
]).
-include("ejabberd.hrl").
start() -> start() ->
%%ejabberd_cover:start(), %%ejabberd_cover:start(),
@ -65,6 +71,33 @@ get_bin_path() ->
Path Path
end. 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() %% @spec () -> false | string()
get_pid_file() -> get_pid_file() ->
case os:getenv("EJABBERD_PID_PATH") of case os:getenv("EJABBERD_PID_PATH") of

View File

@ -23,7 +23,8 @@
%% If the ejabberd application description isn't loaded, returns atom: undefined %% If the ejabberd application description isn't loaded, returns atom: undefined
-define(VERSION, element(2, application:get_key(ejabberd,vsn))). -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(MYNAME, hd(ejabberd_config:get_global_option(hosts))).
-define(MYLANG, ejabberd_config:get_global_option(language)). -define(MYLANG, ejabberd_config:get_global_option(language)).
@ -35,6 +36,8 @@
-define(S2STIMEOUT, 600000). -define(S2STIMEOUT, 600000).
-define(PRIVACY_SUPPORT, true).
%%-define(DBGFSM, true). %%-define(DBGFSM, true).
%% --------------------------------- %% ---------------------------------
@ -58,4 +61,3 @@
-define(CRITICAL_MSG(Format, Args), -define(CRITICAL_MSG(Format, Args),
ejabberd_logger:critical_msg(?MODULE,?LINE,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]), Message = io_lib:format("~s~n~s", [Subject, AnnouncementText]),
lists:foreach( lists:foreach(
fun(ServerHost) -> fun(ServerHost) ->
MUCHost = gen_mod:get_module_opt_host( MUCHost = gen_mod:expand_host_name(
ServerHost, mod_muc, "conference.@HOST@"), ServerHost, mod_muc, "conference"),
MUCHostB = list_to_binary(MUCHost), MUCHostB = list_to_binary(MUCHost),
mod_muc:broadcast_service_message(MUCHostB, Message) mod_muc:broadcast_service_message(MUCHostB, Message)
end, end,

View File

@ -119,6 +119,15 @@ db_init() ->
%% Start all the modules in all the hosts %% Start all the modules in all the hosts
start_modules() -> 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( lists:foreach(
fun(Host) -> fun(Host) ->
case ejabberd_config:get_local_option({modules, Host}) of case ejabberd_config:get_local_option({modules, Host}) of

View File

@ -52,6 +52,15 @@
plain_password_required/1 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]). -export([auth_modules/1]).
-include("ejabberd.hrl"). -include("ejabberd.hrl").
@ -67,13 +76,26 @@
%% @spec () -> term() %% @spec () -> term()
start() -> start() ->
lists:foreach( ?DEBUG("About to start auth modules. Hosts: ~p", ?MYHOSTS),
fun(Host) -> lists:foreach(fun start/1, ?MYHOSTS).
lists:foreach(
fun(M) -> start(Host) ->
M:start(Host) start_modules(Host, auth_modules(Host)).
end, auth_modules(Host))
end, ?MYHOSTS). 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() %% @spec (Server) -> bool()
%% Server = string() %% Server = string()
@ -186,7 +208,7 @@ try_register(User, Server, Password)
true -> true ->
{atomic, exists}; {atomic, exists};
false -> false ->
case lists:member(exmpp_stringprep:nameprep(Server), ?MYHOSTS) of case ?IS_MY_HOST(exmpp_stringprep:nameprep(Server)) of
true -> true ->
Res = lists:foldl( Res = lists:foldl(
fun (_M, {atomic, ok} = Res) -> fun (_M, {atomic, ok} = Res) ->
@ -443,10 +465,13 @@ auth_modules() ->
auth_modules(Server) when is_list(Server) -> auth_modules(Server) when is_list(Server) ->
LServer = exmpp_stringprep:nameprep(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 Methods = if
Method == undefined -> []; Method == undefined -> [];
is_list(Method) -> Method; is_list(Method) -> Method;
is_atom(Method) -> [Method] is_atom(Method) -> [Method]
end, 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, base,
uids, uids,
ufilter, ufilter,
sfilter,
lfilter, %% Local filter (performed by ejabberd, not LDAP) lfilter, %% Local filter (performed by ejabberd, not LDAP)
dn_filter, dn_filter,
dn_filter_attrs dn_filter_attrs
@ -99,21 +98,41 @@ handle_info(_Info, State) ->
%% Host = string() %% Host = string()
start(Host) -> start(Host) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE), ?DEBUG("Starting ~p for ~p.", [?MODULE, Host]),
ChildSpec = { case ejabberd_config:get_host_option(Host, ldap_servers) of
Proc, {?MODULE, start_link, [Host]}, undefined -> check_bad_config(Host);
transient, 1000, worker, [?MODULE] {host, _Host} -> ok;
}, _ ->
supervisor:start_child(ejabberd_sup, ChildSpec). 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() %% @spec (Host) -> term()
%% Host = string() %% Host = string()
stop(Host) -> stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE), case ejabberd_config:get_host_option(Host, ldap_servers) of
gen_server:call(Proc, stop), undefined -> ok;
supervisor:terminate_child(ejabberd_sup, Proc), {host, _Host} -> ok;
supervisor:delete_child(ejabberd_sup, Proc). _ ->
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() %% @spec (Host) -> term()
%% Host = string() %% Host = string()
@ -190,7 +209,7 @@ check_password(User, Server, Password, _Digest, _DigestGen) ->
set_password(User, Server, Password) -> set_password(User, Server, Password) ->
{ok, State} = eldap_utils:get_state(Server, ?MODULE), {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 ->
{error, user_not_found}; {error, user_not_found};
DN -> DN ->
@ -285,8 +304,8 @@ remove_user(_User, _Server, _Password) ->
%% Password = string() %% Password = string()
check_password_ldap(User, Server, Password) -> check_password_ldap(User, Server, Password) ->
{ok, State} = eldap_utils:get_state(Server, ?MODULE), {ok, State} = get_state(Server),
case find_user_dn(User, State) of case find_user_dn(User, Server, State) of
false -> false ->
false; false;
DN -> DN ->
@ -294,7 +313,21 @@ check_password_ldap(User, Server, Password) ->
ok -> true; ok -> true;
_ -> false _ -> false
end 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}] %% @spec (Server) -> [{LUser, LServer}]
%% Server = string() %% Server = string()
@ -303,11 +336,11 @@ check_password_ldap(User, Server, Password) ->
get_vh_registered_users_ldap(Server) -> get_vh_registered_users_ldap(Server) ->
{ok, State} = eldap_utils:get_state(Server, ?MODULE), {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, Eldap_ID = State#state.eldap_id,
Server = State#state.host, SearchFilter = build_sfilter(State, UIDs),
ResAttrs = result_attrs(State), ResAttrs = result_attrs(State),
case eldap_filter:parse(State#state.sfilter) of case eldap_filter:parse(SearchFilter) of
{ok, EldapFilter} -> {ok, EldapFilter} ->
case eldap_pool:search(Eldap_ID, [{base, State#state.base}, case eldap_pool:search(Eldap_ID, [{base, State#state.base},
{filter, EldapFilter}, {filter, EldapFilter},
@ -317,7 +350,7 @@ get_vh_registered_users_ldap(Server) ->
lists:flatmap( lists:flatmap(
fun(#eldap_entry{attributes = Attrs, fun(#eldap_entry{attributes = Attrs,
object_name = DN}) -> object_name = DN}) ->
case is_valid_dn(DN, Attrs, State) of case is_valid_dn(DN, Server, Attrs, State) of
false -> []; false -> [];
_ -> _ ->
case eldap_utils:find_ldap_attrs(UIDs, Attrs) of 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) -> is_user_exists_ldap(User, Server) ->
{ok, State} = eldap_utils:get_state(Server, ?MODULE), {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; false -> false;
_DN -> true _DN -> true
end. end.
@ -363,16 +396,17 @@ handle_call(stop, _From, State) ->
handle_call(_Request, _From, State) -> handle_call(_Request, _From, State) ->
{reply, bad_request, State}. {reply, bad_request, State}.
find_user_dn(User, State) -> find_user_dn(User, Server, State) ->
ResAttrs = result_attrs(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} -> {ok, Filter} ->
case eldap_pool:search(State#state.eldap_id, [{base, State#state.base}, case eldap_pool:search(State#state.eldap_id, [{base, State#state.base},
{filter, Filter}, {filter, Filter},
{attributes, ResAttrs}]) of {attributes, ResAttrs}]) of
#eldap_search_result{entries = [#eldap_entry{attributes = Attrs, #eldap_search_result{entries = [#eldap_entry{attributes = Attrs,
object_name = DN} | _]} -> object_name = DN} | _]} ->
dn_filter(DN, Attrs, State); dn_filter(DN, Server, Attrs, State);
_ -> _ ->
false false
end; end;
@ -381,20 +415,20 @@ find_user_dn(User, State) ->
end. end.
%% apply the dn filter and the local filter: %% 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) %% Check if user is denied access by attribute value (local check)
case check_local_filter(Attrs, State) of case check_local_filter(Attrs, State) of
false -> false; false -> false;
true -> is_valid_dn(DN, Attrs, State) true -> is_valid_dn(DN, Server, Attrs, State)
end. end.
%% Check that the DN is valid, based on the dn filter %% 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; DN;
is_valid_dn(DN, Attrs, State) -> is_valid_dn(DN, Server, Attrs, State) ->
DNAttrs = State#state.dn_filter_attrs, 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], Values = [{"%s", eldap_utils:get_ldap_attr(Attr, Attrs), 1} || Attr <- DNAttrs],
SubstValues = case eldap_utils:find_ldap_attrs(UIDs, Attrs) of SubstValues = case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
"" -> Values; "" -> Values;
@ -449,6 +483,22 @@ result_attrs(#state{uids = UIDs, dn_filter_attrs = DNFilterAttrs}) ->
[UID | Acc] [UID | Acc]
end, DNFilterAttrs, UIDs). 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 %%% Auxiliary functions
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
@ -480,15 +530,12 @@ parse_options(Host) ->
end, end,
UIDs = case ejabberd_config:get_local_option({ldap_uids, Host}) of UIDs = case ejabberd_config:get_local_option({ldap_uids, Host}) of
undefined -> [{"uid", "%u"}]; undefined -> [{"uid", "%u"}];
UI -> eldap_utils:uids_domain_subst(Host, UI) UI -> UI
end, end,
SubFilter = lists:flatten(eldap_utils:generate_subfilter(UIDs)),
UserFilter = case ejabberd_config:get_local_option({ldap_filter, Host}) of UserFilter = case ejabberd_config:get_local_option({ldap_filter, Host}) of
undefined -> SubFilter; undefined -> "";
"" -> SubFilter; F -> F
F -> "(&" ++ SubFilter ++ F ++ ")"
end, end,
SearchFilter = eldap_filter:do_sub(UserFilter, [{"%u", "*"}]),
LDAPBase = ejabberd_config:get_local_option({ldap_base, Host}), LDAPBase = ejabberd_config:get_local_option({ldap_base, Host}),
{DNFilter, DNFilterAttrs} = {DNFilter, DNFilterAttrs} =
case ejabberd_config:get_local_option({ldap_dn_filter, Host}) of case ejabberd_config:get_local_option({ldap_dn_filter, Host}) of
@ -513,7 +560,6 @@ parse_options(Host) ->
base = LDAPBase, base = LDAPBase,
uids = UIDs, uids = UIDs,
ufilter = UserFilter, ufilter = UserFilter,
sfilter = SearchFilter,
lfilter = LocalFilter, lfilter = LocalFilter,
dn_filter = DNFilter, dn_filter = DNFilter,
dn_filter_attrs = DNFilterAttrs dn_filter_attrs = DNFilterAttrs

View File

@ -29,6 +29,7 @@
%% External exports %% External exports
-export([start/1, -export([start/1,
stop/1,
set_password/3, set_password/3,
check_password/3, check_password/3,
check_password/5, check_password/5,
@ -55,8 +56,14 @@
%% @spec (Host) -> ok %% @spec (Host) -> ok
%% Host = string() %% Host = string()
start(_Host) -> start(Host) ->
ok. case ejabberd_odbc:running(Host) of
true -> ok;
false -> ejabberd_rdbms:start_odbc(Host)
end.
stop(Host) ->
ejabberd_rdbms:stop_odbc(Host).
%% @spec () -> bool() %% @spec () -> bool()

View File

@ -315,7 +315,7 @@ wait_for_stream({xmlstreamstart, #xmlel{ns = NS} = Opening}, StateData) ->
ServerB = exmpp_stringprep:nameprep( ServerB = exmpp_stringprep:nameprep(
exmpp_stream:get_receiving_entity(Opening)), exmpp_stream:get_receiving_entity(Opening)),
Server = binary_to_list(ServerB), Server = binary_to_list(ServerB),
case lists:member(Server, ?MYHOSTS) of case ?IS_MY_HOST(Server) of
true -> true ->
Lang = exmpp_stream:get_lang(Opening), Lang = exmpp_stream:get_lang(Opening),
change_shaper(StateData, change_shaper(StateData,

View File

@ -46,6 +46,8 @@ config() ->
check_database_modules() -> check_database_modules() ->
[check_database_module(M)||M<-get_db_used()]. [check_database_module(M)||M<-get_db_used()].
check_database_module(host) ->
ok;
check_database_module(odbc) -> check_database_module(odbc) ->
check_modules(odbc, [odbc, odbc_app, odbc_sup, ejabberd_odbc, ejabberd_odbc_sup, odbc_queries]); check_modules(odbc, [odbc, odbc_app, odbc_sup, ejabberd_odbc, ejabberd_odbc_sup, odbc_queries]);
check_database_module(mysql) -> check_database_module(mysql) ->

View File

@ -27,17 +27,32 @@
-module(ejabberd_config). -module(ejabberd_config).
-author('alexey@process-one.net'). -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, 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, del_global_option/1, del_local_option/1,
get_global_option/1, get_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([get_vh_by_auth_method/1]).
-export([is_file_readable/1]). -export([is_file_readable/1]).
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("ejabberd_config.hrl"). -include("ejabberd_config.hrl").
-include_lib("kernel/include/file.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()} %% @type macro() = {macro_key(), macro_value()}
@ -153,6 +168,14 @@ search_hosts(Term, State) ->
add_hosts_to_option(Hosts, State) -> add_hosts_to_option(Hosts, State) ->
PrepHosts = normalize_hosts(Hosts), 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}). add_option(hosts, PrepHosts, State#state{hosts = PrepHosts}).
normalize_hosts(Hosts) -> normalize_hosts(Hosts) ->
@ -379,6 +402,8 @@ process_term(Term, State) ->
{host_config, Host, Terms} -> {host_config, Host, Terms} ->
lists:foldl(fun(T, S) -> process_host_term(T, Host, S) end, lists:foldl(fun(T, S) -> process_host_term(T, Host, S) end,
State, Terms); State, Terms);
{clusterid, ClusterID} ->
add_option(clusterid, ClusterID, State);
{listen, Listeners} -> {listen, Listeners} ->
Listeners2 = Listeners2 =
lists:map( lists:map(
@ -443,8 +468,7 @@ process_term(Term, State) ->
{max_fsm_queue, N} -> {max_fsm_queue, N} ->
add_option(max_fsm_queue, N, State); add_option(max_fsm_queue, N, State);
{_Opt, _Val} -> {_Opt, _Val} ->
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end, process_host_term(Term, global, State)
State, State#state.hosts)
end. end.
process_host_term(Term, Host, State) -> process_host_term(Term, Host, State) ->
@ -569,10 +593,11 @@ add_global_option(Opt, Val) ->
end). end).
add_local_option(Opt, Val) -> add_local_option(Opt, Val) ->
mnesia:transaction(fun() -> mnesia:transaction(fun mne_add_local_option/2, [Opt, Val]).
mnesia:write(#local_config{key = Opt,
value = Val}) mne_add_local_option(Opt, Val) ->
end). mnesia:write(#local_config{key = Opt,
value = Val}).
del_global_option(Opt) -> del_global_option(Opt) ->
mnesia:transaction(fun() -> mnesia:transaction(fun() ->
@ -585,6 +610,13 @@ del_local_option(Opt) ->
end). 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) -> get_global_option(Opt) ->
case ets:lookup(config, Opt) of case ets:lookup(config, Opt) of
[#config{value = Val}] -> [#config{value = Val}] ->
@ -593,6 +625,13 @@ get_global_option(Opt) ->
undefined undefined
end. 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) -> get_local_option(Opt) ->
case ets:lookup(local_config, Opt) of case ets:lookup(local_config, Opt) of
[#local_config{value = Val}] -> [#local_config{value = Val}] ->
@ -601,6 +640,17 @@ get_local_option(Opt) ->
undefined undefined
end. 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 %% Return the list of hosts handled by a given module
get_vh_by_auth_method(AuthMethod) -> get_vh_by_auth_method(AuthMethod) ->
mnesia:dirty_select(local_config, mnesia:dirty_select(local_config,
@ -619,3 +669,54 @@ is_file_readable(Path) ->
{error, _Reason} -> {error, _Reason} ->
false false
end. 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(config, {key, value}).
-record(local_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. %% @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. %% If a call returns stop, no more calls are performed.
run(Hook, Args) -> run(Hook, Args) ->
run(Hook, global, Args). runx(Hook, global, Args).
run(Hook, Host, Args) when is_binary(Host) orelse is_atom(Host) -> run(Hook, Host, Args) when is_binary(Host) ->
case ets:lookup(hooks, {Hook, Host}) of 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}] -> [{_, Ls}] ->
run1(Ls, Hook, Args); run1(Ls, Hook, Args);
[] -> [] ->
ok case ets:lookup(hooks, {Hook, global}) of
[{_, Ls}] ->
run1(Ls, Hook, Args);
[] ->
ok
end
end. end.
%% @spec (Hook::atom(), Val, Args) -> Val | stopped | NewVal %% @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 '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. %% If a call returns {stopped, NewVal}, no more calls are performed and NewVal is returned.
run_fold(Hook, Val, Args) -> 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 %% @spec (Hook::atom(), Host, Val, Args) -> Val | stopped | NewVal
%% Host = global | binary() %% Host = global | binary()
run_fold(Hook, Host, Val, Args) when is_binary(Host) orelse is_atom(Host) -> run_fold(Hook, Host, Val, Args) when is_binary(Host) ->
case ets:lookup(hooks, {Hook, Host}) of 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}] -> [{_, Ls}] ->
run_fold1(Ls, Hook, Val, Args); run_fold1(Ls, Hook, Val, Args);
[] -> [] ->
Val case ets:lookup(hooks, {Hook, global}) of
[{_, Ls}] ->
run_fold1(Ls, Hook, Val, Args);
[] ->
Val
end
end. end.
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
@ -173,7 +199,8 @@ init([]) ->
%% {stop, Reason, State} (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called)
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
handle_call({add, Hook, Host, Module, Function, Seq}, _From, State) -> 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}] -> [{_, Ls}] ->
El = {Seq, Module, Function}, El = {Seq, Module, Function},
case lists:member(El, Ls) of case lists:member(El, Ls) of
@ -181,12 +208,12 @@ handle_call({add, Hook, Host, Module, Function, Seq}, _From, State) ->
ok; ok;
false -> false ->
NewLs = lists:merge(Ls, [El]), NewLs = lists:merge(Ls, [El]),
ets:insert(hooks, {{Hook, Host}, NewLs}), ets:insert(hooks, {{Hook, NHost}, NewLs}),
ok ok
end; end;
[] -> [] ->
NewLs = [{Seq, Module, Function}], NewLs = [{Seq, Module, Function}],
ets:insert(hooks, {{Hook, Host}, NewLs}), ets:insert(hooks, {{Hook, NHost}, NewLs}),
ok ok
end, end,
{reply, Reply, State}; {reply, Reply, State};
@ -209,10 +236,11 @@ handle_call({add, Hook, Host, Node, Module, Function, Seq}, _From, State) ->
end, end,
{reply, Reply, State}; {reply, Reply, State};
handle_call({delete, Hook, Host, Module, Function, Seq}, _From, 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}] -> [{_, Ls}] ->
NewLs = lists:delete({Seq, Module, Function}, Ls), NewLs = lists:delete({Seq, Module, Function}, Ls),
ets:insert(hooks, {{Hook, Host}, NewLs}), ets:insert(hooks, {{Hook, NHost}, NewLs}),
ok; ok;
[] -> [] ->
ok ok

View File

@ -82,7 +82,7 @@ process_iq(From, To, Packet) ->
case exmpp_iq:xmlel_to_iq(Packet) of case exmpp_iq:xmlel_to_iq(Packet) of
#iq{kind = request, ns = XMLNS} = IQ_Rec -> #iq{kind = request, ns = XMLNS} = IQ_Rec ->
Host = exmpp_jid:prep_domain(To), 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}] -> [{_, Module, Function}] ->
ResIQ = Module:Function(From, To, IQ_Rec), ResIQ = Module:Function(From, To, IQ_Rec),
if if
@ -96,8 +96,15 @@ process_iq(From, To, Packet) ->
gen_iq_handler:handle(Host, Module, Function, Opts, gen_iq_handler:handle(Host, Module, Function, Opts,
From, To, IQ_Rec); From, To, IQ_Rec);
[] -> [] ->
Err = exmpp_iq:error(Packet, 'feature-not-implemented'), case ets:lookup(?IQTABLE, {XMLNS, global}) of
ejabberd_router:route(To, From, Err) [{_, 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; end;
#iq{kind = response} = IQReply -> #iq{kind = response} = IQReply ->
%%IQReply = jlib:iq_query_or_response_info(IQ_Rec), %%IQReply = jlib:iq_query_or_response_info(IQ_Rec),
@ -193,10 +200,10 @@ bounce_resource_packet(From, To, Packet) ->
init([]) -> init([]) ->
lists:foreach( lists:foreach(
fun(Host) -> fun(Host) ->
ejabberd_router:register_route(Host, {apply, ?MODULE, route}), ejabberd_router:register_route(Host, {apply, ?MODULE, route})
ejabberd_hooks:add(local_send_to_resource_hook, list_to_binary(Host),
?MODULE, bounce_resource_packet, 100)
end, ?MYHOSTS), end, ?MYHOSTS),
ejabberd_hooks:add(local_send_to_resource_hook, global,
?MODULE, bounce_resource_packet, 100),
catch ets:new(?IQTABLE, [named_table, public]), catch ets:new(?IQTABLE, [named_table, public]),
mnesia:delete_table(iq_response), mnesia:delete_table(iq_response),
catch ets:new(iq_response, [named_table, public, catch ets:new(iq_response, [named_table, public,
@ -253,21 +260,21 @@ handle_info({route, From, To, Packet}, State) ->
end, end,
{noreply, State}; {noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function}, 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), catch mod_disco:register_feature(Host, XMLNS),
{noreply, State}; {noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, 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), catch mod_disco:register_feature(Host, XMLNS),
{noreply, State}; {noreply, State};
handle_info({unregister_iq_handler, Host, XMLNS}, 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}] -> [{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts); gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ -> _ ->
ok ok
end, end,
ets:delete(?IQTABLE, {XMLNS, Host}), ets:delete(?IQTABLE, {XMLNS, ejabberd:normalize_host(Host)}),
catch mod_disco:unregister_feature(Host, XMLNS), catch mod_disco:unregister_feature(Host, XMLNS),
{noreply, State}; {noreply, State};
handle_info(refresh_iq_handlers, State) -> handle_info(refresh_iq_handlers, State) ->

View File

@ -27,8 +27,15 @@
-module(ejabberd_rdbms). -module(ejabberd_rdbms).
-author('alexey@process-one.net'). -author('alexey@process-one.net').
-export([start/0]). -export([start/0
,start_odbc/1
,stop_odbc/1
,running/1
]).
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("ejabberd_config.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-define(SUPERVISOR, ejabberd_sup).
start() -> start() ->
%% Check if ejabberd has been compiled with ODBC %% Check if ejabberd has been compiled with ODBC
@ -52,22 +59,39 @@ start_hosts() ->
%% Start the ODBC module on the given host %% Start the ODBC module on the given host
start_odbc(Host) -> start_odbc(Host) ->
Supervisor_name = gen_mod:get_module_proc(Host, ejabberd_odbc_sup), SupervisorName = sup_name(Host),
ChildSpec = ChildSpec =
{Supervisor_name, {SupervisorName,
{ejabberd_odbc_sup, start_link, [Host]}, {ejabberd_odbc_sup, start_link, [Host]},
transient, transient,
infinity, infinity,
supervisor, supervisor,
[ejabberd_odbc_sup]}, [ejabberd_odbc_sup]},
case supervisor:start_child(ejabberd_sup, ChildSpec) of case supervisor:start_child(?SUPERVISOR, ChildSpec) of
{ok, _PID} -> {ok, _PID} ->
ok; ok;
_Error -> _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) start_odbc(Host)
end. 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 %% Returns true if we have configured odbc_server for the given host
needs_odbc(Host) -> needs_odbc(Host) ->
try try
@ -75,9 +99,32 @@ needs_odbc(Host) ->
case ejabberd_config:get_local_option({odbc_server, LHost}) of case ejabberd_config:get_local_option({odbc_server, LHost}) of
undefined -> undefined ->
false; false;
_ -> true {host, _} ->
false;
_ ->
true
end end
catch catch
_ -> _ ->
false false
end. 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_route/2,
register_routes/1, register_routes/1,
unregister_route/1, unregister_route/1,
force_unregister_route/1,
unregister_routes/1, unregister_routes/1,
dirty_get_all_routes/0, dirty_get_all_routes/0,
dirty_get_all_domains/0, dirty_get_all_domains/0,
read_route/1,
make_id/0 make_id/0
]). ]).
@ -98,56 +100,24 @@ route_error(From, To, ErrPacket, OrigPacket) ->
ok ok
end. 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, undefined).
register_route(Domain, LocalHint) -> register_route(Domain, LocalHint) ->
try try
LDomain = exmpp_stringprep:nameprep(Domain), LDomain = exmpp_stringprep:nameprep(Domain),
LDomainB = list_to_binary(LDomain), LDomainB = list_to_binary(LDomain),
Pid = self(), Pid = self(),
case get_component_number(LDomain) of case get_component_number(LDomain) of
undefined -> undefined ->
F = fun() -> mnesia:transaction(fun register_simple_route/3, [LDomainB, Pid, LocalHint]);
mnesia:write(#route{domain = LDomainB, N ->
pid = Pid, mnesia:transaction(fun register_balanced_route/3, [LDomainB, Pid, N])
local_hint = LocalHint}) end
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
catch catch
_ -> _ ->
erlang:error({invalid_domain, Domain}) erlang:error({invalid_domain, Domain})
end. end.
@ -156,53 +126,112 @@ register_routes(Domains) ->
register_route(Domain) register_route(Domain)
end, Domains). 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 try
LDomain = exmpp_stringprep:nameprep(Domain), LDomain = exmpp_stringprep:nameprep(Domain),
LDomainB = list_to_binary(LDomain), LDomainB = list_to_binary(LDomain),
Pid = self(), Pid = self(),
case get_component_number(LDomain) of case get_component_number(LDomain) of
undefined -> undefined ->
F = fun() -> mnesia:transaction(fun delete_simple_route/2, [LDomainB, Pid]);
case mnesia:match_object(
#route{domain = LDomainB,
pid = Pid,
_ = '_'}) of
[R] ->
mnesia:delete_object(R);
_ ->
ok
end
end,
mnesia:transaction(F);
_ -> _ ->
F = fun() -> mnesia:transaction(fun delete_balanced_route/2, [LDomainB, Pid])
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)
end end
catch catch
_ -> _ ->
erlang:error({invalid_domain, Domain}) erlang:error({invalid_domain, Domain})
end. 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) -> unregister_routes(Domains) ->
lists:foreach(fun(Domain) -> lists:foreach(fun(Domain) ->
unregister_route(Domain) unregister_route(Domain)
end, Domains). 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() -> dirty_get_all_routes() ->
lists:usort( lists:usort(
@ -376,10 +405,16 @@ do_route(OrigFrom, OrigTo, OrigPacket) ->
case ejabberd_hooks:run_fold(filter_packet, case ejabberd_hooks:run_fold(filter_packet,
{OrigFrom, OrigTo, OrigPacket}, []) of {OrigFrom, OrigTo, OrigPacket}, []) of
{From, To, Packet} -> {From, To, Packet} ->
LDomain = exmpp_jid:prep_domain(To), LDstDomain = exmpp_jid:prep_domain_as_list(To),
case mnesia:dirty_read(route, LDomain) of 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] -> [R] ->
Pid = R#route.pid, Pid = R#route.pid,
if if
@ -396,7 +431,6 @@ do_route(OrigFrom, OrigTo, OrigPacket) ->
drop drop
end; end;
Rs -> Rs ->
LDstDomain = exmpp_jid:prep_domain_as_list(To),
Value = case ejabberd_config:get_local_option( Value = case ejabberd_config:get_local_option(
{domain_balancing, LDstDomain}) of {domain_balancing, LDstDomain}) of
undefined -> now(); undefined -> now();

View File

@ -353,6 +353,12 @@ init([]) ->
mnesia:add_table_index(session, us), mnesia:add_table_index(session, us),
mnesia:add_table_copy(session, node(), ram_copies), mnesia:add_table_copy(session, node(), ram_copies),
ets:new(sm_iqtable, [named_table]), 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), ejabberd_hooks:add(node_hash_update, ?MODULE, migrate, 100),
lists:foreach( lists:foreach(
fun(Host) -> fun(Host) ->
@ -417,19 +423,19 @@ handle_info({route, From, To, Packet}, State) ->
end, end,
{noreply, State}; {noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function}, 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}; {noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, 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}; {noreply, State};
handle_info({unregister_iq_handler, Host, XMLNS}, 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}] -> [{_, Module, Function, Opts}] ->
gen_iq_handler:stop_iq_handler(Module, Function, Opts); gen_iq_handler:stop_iq_handler(Module, Function, Opts);
_ -> _ ->
ok ok
end, end,
ets:delete(sm_iqtable, {XMLNS, Host}), ets:delete(sm_iqtable, {XMLNS, ejabberd:normalize_host(Host)}),
{noreply, State}; {noreply, State};
handle_info(_Info, State) -> handle_info(_Info, State) ->
{noreply, State}. {noreply, State}.
@ -763,7 +769,7 @@ process_iq(From, To, Packet) ->
case exmpp_iq:xmlel_to_iq(Packet) of case exmpp_iq:xmlel_to_iq(Packet) of
#iq{kind = request, ns = XMLNS} = IQ_Rec -> #iq{kind = request, ns = XMLNS} = IQ_Rec ->
LServer = exmpp_jid:prep_domain(To), 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}] -> [{_, Module, Function}] ->
ResIQ = Module:Function(From, To, IQ_Rec), ResIQ = Module:Function(From, To, IQ_Rec),
if if
@ -774,11 +780,17 @@ process_iq(From, To, Packet) ->
ok ok
end; end;
[{_, Module, Function, Opts}] -> [{_, Module, Function, Opts}] ->
gen_iq_handler:handle(LServer, gen_iq_handler:handle(LServer,
Module, Function, Opts, From, To, IQ_Rec); Module, Function, Opts, From, To, IQ_Rec);
[] -> [] ->
Err = exmpp_iq:error(Packet, 'service-unavailable'), case ets:lookup(sm_iqtable, {XMLNS, global}) of
ejabberd_router:route(To, From, Err) [{_, 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; end;
#iq{kind = response} -> #iq{kind = response} ->
ok; ok;

View File

@ -42,13 +42,13 @@ init([]) ->
brutal_kill, brutal_kill,
worker, worker,
[ejabberd_hooks]}, [ejabberd_hooks]},
SystemMonitor = GlobalRouter =
{ejabberd_system_monitor, {ejabberd_global_router,
{ejabberd_system_monitor, start_link, []}, {ejabberd_global_router, start_link, []},
permanent, permanent,
brutal_kill, brutal_kill,
worker, worker,
[ejabberd_system_monitor]}, [ejabberd_global_router]},
Router = Router =
{ejabberd_router, {ejabberd_router,
{ejabberd_router, start_link, []}, {ejabberd_router, start_link, []},
@ -137,6 +137,13 @@ init([]) ->
infinity, infinity,
supervisor, supervisor,
[ejabberd_tmp_sup]}, [ejabberd_tmp_sup]},
Hosts =
{ejabberd_hosts,
{ejabberd_hosts, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_hosts]},
HTTPSupervisor = HTTPSupervisor =
{ejabberd_http_sup, {ejabberd_http_sup,
{ejabberd_tmp_sup, start_link, {ejabberd_tmp_sup, start_link,
@ -186,8 +193,8 @@ init([]) ->
[ejabberd_cluster]}, [ejabberd_cluster]},
{ok, {{one_for_one, 10, 1}, {ok, {{one_for_one, 10, 1},
[Hooks, [Hooks,
GlobalRouter,
Cluster, Cluster,
SystemMonitor,
Router, Router,
Router_multicast, Router_multicast,
SM, SM,
@ -199,6 +206,7 @@ init([]) ->
S2SInSupervisor, S2SInSupervisor,
S2SOutSupervisor, S2SOutSupervisor,
ServiceSupervisor, ServiceSupervisor,
Hosts,
HTTPSupervisor, HTTPSupervisor,
HTTPPollSupervisor, HTTPPollSupervisor,
IQSupervisor, IQSupervisor,

View File

@ -57,8 +57,9 @@ if [ "$ERLANG_NODE_ARG" != "" ] ; then
fi fi
# check the proper system user is used # check the proper system user is used
ID=`id -g` if [ `uname -s` = "SunOS" ]; then IDCMD=/usr/xpg4/bin/id; else IDCMD=id; fi
GIDS=`id -G` ID=`$IDCMD -g`
EJID=`$IDCMD -g $INSTALLUSER`
EJID=`id -g $INSTALLUSER` EJID=`id -g $INSTALLUSER`
EXEC_CMD="false" EXEC_CMD="false"
for GID in $GIDS; do for GID in $GIDS; do
@ -77,10 +78,13 @@ fi
NAME=-name NAME=-name
[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && NAME=-sname [ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && NAME=-sname
if [ "$FIREWALL_WINDOW" = "" ] ; then KERNEL_OPTS=""
KERNEL_OPTS="" if [ "$FIREWALL_WINDOW" != "" ] ; then
else KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
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 fi
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS" ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS"
@ -146,7 +150,7 @@ start ()
$KERNEL_OPTS \ $KERNEL_OPTS \
-s ejabberd \ -s ejabberd \
-sasl sasl_error_logger \\{file,\\\"$SASL_LOG_PATH\\\"\\} \ -sasl sasl_error_logger \\{file,\\\"$SASL_LOG_PATH\\\"\\} \
$ERLANG_OPTS $ARGS \"$@\" $ERL_ARGS" $ERLANG_OPTS -- $ARGS \"$@\" $ERL_ARGS"
} }
# attach to server # attach to server
@ -177,7 +181,7 @@ debug ()
$NAME debug-${TTY}-${ERLANG_NODE} \ $NAME debug-${TTY}-${ERLANG_NODE} \
-remsh $ERLANG_NODE \ -remsh $ERLANG_NODE \
$KERNEL_OPTS \ $KERNEL_OPTS \
$ERLANG_OPTS $ARGS \"$@\" $ERL_ARGS" $ERLANG_OPTS -- $ARGS \"$@\" $ERL_ARGS"
} }
# start interactive server # start interactive server
@ -209,7 +213,7 @@ live ()
-mnesia dir \"\\\"$SPOOLDIR\\\"\" \ -mnesia dir \"\\\"$SPOOLDIR\\\"\" \
$KERNEL_OPTS \ $KERNEL_OPTS \
-s ejabberd \ -s ejabberd \
$ERLANG_OPTS $ARGS \"$@\" $ERL_ARGS" $ERLANG_OPTS -- $ARGS \"$@\" $ERL_ARGS"
} }
etop() etop()

View File

@ -261,7 +261,7 @@ export_private_storage(ServerS, Output) ->
LXMLNS = ejabberd_odbc:escape(atom_to_list(XMLNS)), LXMLNS = ejabberd_odbc:escape(atom_to_list(XMLNS)),
SData = ejabberd_odbc:escape( SData = ejabberd_odbc:escape(
exmpp_xml:document_to_list(Data)), 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) -> (_Host, _R) ->
[] []
end). end).

View File

@ -40,6 +40,7 @@
loaded_modules_with_opts/1, loaded_modules_with_opts/1,
get_hosts/2, get_hosts/2,
get_module_proc/2, get_module_proc/2,
expand_host_name/3,
is_loaded/2]). is_loaded/2]).
-export([behaviour_info/1]). -export([behaviour_info/1]).
@ -192,6 +193,9 @@ loaded_modules_with_opts(Host) ->
[], [],
[{{'$1', '$2'}}]}]). [{{'$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) -> set_module_opts_mnesia(Host, Module, Opts) ->
Modules = case ejabberd_config:get_local_option({modules, Host}) of Modules = case ejabberd_config:get_local_option({modules, Host}) of
undefined -> undefined ->
@ -203,6 +207,9 @@ set_module_opts_mnesia(Host, Module, Opts) ->
Modules2 = [{Module, Opts} | Modules1], Modules2 = [{Module, Opts} | Modules1],
ejabberd_config:add_local_option({modules, Host}, Modules2). 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) -> del_module_mnesia(Host, Module) ->
Modules = case ejabberd_config:get_local_option({modules, Host}) of Modules = case ejabberd_config:get_local_option({modules, Host}) of
undefined -> undefined ->
@ -226,6 +233,8 @@ get_hosts(Opts, Prefix) ->
Hosts Hosts
end. end.
get_module_proc(global, Base) ->
list_to_atom(atom_to_list(Base) ++ "__global");
get_module_proc(Host, {frontend, Base}) -> get_module_proc(Host, {frontend, Base}) ->
get_module_proc("frontend_" ++ Host, Base); get_module_proc("frontend_" ++ Host, Base);
get_module_proc(Host, Base) -> get_module_proc(Host, Base) ->
@ -234,3 +243,11 @@ get_module_proc(Host, Base) ->
is_loaded(Host, Module) -> is_loaded(Host, Module) ->
ets:member(ejabberd_modules, {Module, Host}). 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 %% External exports
-export([start/1, start_link/2, -export([start/1, start_link/2,
running/1,
sql_query/2, sql_query/2,
sql_query_t/1, sql_query_t/1,
sql_transaction/2, sql_transaction/2,
@ -106,6 +107,14 @@ sql_query_on_all_connections(Host, Query) ->
erlang:now()}, ?TRANSACTION_TIMEOUT) end, erlang:now()}, ?TRANSACTION_TIMEOUT) end,
lists:map(F, ejabberd_odbc_sup:get_pids(Host)). 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 %% SQL transaction based on a list of queries
%% This function automatically %% This function automatically
sql_transaction(Host, Queries) when is_list(Queries) -> sql_transaction(Host, Queries) when is_list(Queries) ->
@ -497,7 +506,11 @@ pgsql_to_odbc({ok, PGSQLResult}) ->
pgsql_item_to_odbc({"SELECT", Rows, Recs}) -> pgsql_item_to_odbc({"SELECT", Rows, Recs}) ->
{selected, {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]}; [list_to_tuple(Rec) || Rec <- Recs]};
pgsql_item_to_odbc("INSERT " ++ OIDN) -> pgsql_item_to_odbc("INSERT " ++ OIDN) ->
[_OID, N] = string:tokens(OIDN, " "), [_OID, N] = string:tokens(OIDN, " "),
@ -555,6 +568,7 @@ log(Level, Format, Args) ->
?ERROR_MSG(Format, Args) ?ERROR_MSG(Format, Args)
end. end.
%% TODO: update this function to handle the case clase {host, VhostName}
db_opts(Host) -> db_opts(Host) ->
case ejabberd_config:get_local_option({odbc_server, Host}) of case ejabberd_config:get_local_option({odbc_server, Host}) of
%% Default pgsql port %% Default pgsql port

View File

@ -100,11 +100,16 @@ init([Host]) ->
end, lists:seq(1, PoolSize))}}. end, lists:seq(1, PoolSize))}}.
get_pids(Host) -> get_pids(Host) ->
Rs = mnesia:dirty_read(sql_pool, Host), case ejabberd_config:get_local_option({odbc_server, Host}) of
[R#sql_pool.pid || R <- Rs]. {host, Host1} ->
get_pids(Host1);
_ ->
Rs = mnesia:dirty_read(sql_pool, Host),
[R#sql_pool.pid || R <- Rs]
end.
get_random_pid(Host) -> get_random_pid(Host) ->
Pids = get_pids(Host), Pids = get_pids(ejabberd:normalize_host(Host)),
lists:nth(erlang:phash(now(), length(Pids)), Pids). lists:nth(erlang:phash(now(), length(Pids)), Pids).
add_pid(Host, Pid) -> add_pid(Host, Pid) ->

View File

@ -20,21 +20,35 @@
-- Needs MySQL (at least 4.0.x) with innodb back-end -- Needs MySQL (at least 4.0.x) with innodb back-end
SET table_type=InnoDB; 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 ( CREATE TABLE users (
username varchar(250) PRIMARY KEY, host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
password text 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; ) CHARACTER SET utf8;
CREATE TABLE last ( CREATE TABLE last (
username varchar(250) PRIMARY KEY, host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
seconds text NOT NULL, seconds text NOT NULL,
state text NOT NULl state text NOT NULL,
PRIMARY KEY (host, username)
) CHARACTER SET utf8; ) CHARACTER SET utf8;
CREATE TABLE rosterusers ( CREATE TABLE rosterusers (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL, username varchar(250) NOT NULL,
jid varchar(250) NOT NULL, jid varchar(250) NOT NULL,
nick text NOT NULL, nick text NOT NULL,
@ -44,42 +58,44 @@ CREATE TABLE rosterusers (
server character(1) NOT NULL, server character(1) NOT NULL,
subscribe text NOT NULL, subscribe text NOT NULL,
type text, 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; ) 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_username ON rosterusers(username);
CREATE INDEX i_rosteru_jid ON rosterusers(jid); CREATE INDEX i_rosteru_jid ON rosterusers(jid);
CREATE TABLE rostergroups ( CREATE TABLE rostergroups (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL, username varchar(250) NOT NULL,
jid 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; ) CHARACTER SET utf8;
CREATE INDEX pk_rosterg_user_jid ON rostergroups(username(75), jid(75));
CREATE TABLE spool ( CREATE TABLE spool (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL, username varchar(250) NOT NULL,
xml text NOT NULL, xml text NOT NULL,
seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, 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; ) CHARACTER SET utf8;
CREATE INDEX i_despool USING BTREE ON spool(username);
CREATE TABLE vcard ( CREATE TABLE vcard (
username varchar(250) PRIMARY KEY, host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
vcard text 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; ) CHARACTER SET utf8;
CREATE TABLE vcard_search ( CREATE TABLE vcard_search (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL, username varchar(250) NOT NULL,
lusername varchar(250) PRIMARY KEY, lusername varchar(250) NOT NULL,
fn text NOT NULL, fn text NOT NULL,
lfn varchar(250) NOT NULL, lfn varchar(250) NOT NULL,
family text NOT NULL, family text NOT NULL,
@ -101,7 +117,8 @@ CREATE TABLE vcard_search (
orgname text NOT NULL, orgname text NOT NULL,
lorgname varchar(250) NOT NULL, lorgname varchar(250) NOT NULL,
orgunit text NOT NULL, orgunit text NOT NULL,
lorgunit varchar(250) NOT NULL lorgunit varchar(250) NOT NULL,
PRIMARY KEY (host, lusername)
) CHARACTER SET utf8; ) CHARACTER SET utf8;
CREATE INDEX i_vcard_search_lfn ON vcard_search(lfn); 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 INDEX i_vcard_search_lorgunit ON vcard_search(lorgunit);
CREATE TABLE privacy_default_list ( CREATE TABLE privacy_default_list (
username varchar(250) PRIMARY KEY, host varchar(250) NOT NULL,
name varchar(250) NOT NULL username varchar(250),
name varchar(250) NOT NULL,
PRIMARY KEY (host, username)
) CHARACTER SET utf8; ) CHARACTER SET utf8;
CREATE TABLE privacy_list ( CREATE TABLE privacy_list (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL, username varchar(250) NOT NULL,
name varchar(250) NOT NULL, name varchar(250) NOT NULL,
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, 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; ) 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 ( CREATE TABLE privacy_list_data (
id bigint, id bigint,
t character(1) NOT NULL, t character(1) NOT NULL,
@ -145,14 +163,15 @@ CREATE TABLE privacy_list_data (
) CHARACTER SET utf8; ) CHARACTER SET utf8;
CREATE TABLE private_storage ( CREATE TABLE private_storage (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL, username varchar(250) NOT NULL,
namespace varchar(250) NOT NULL, namespace varchar(250) NOT NULL,
data text 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; ) CHARACTER SET utf8;
CREATE INDEX i_private_storage_username USING BTREE ON private_storage(username); 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 -- Not tested in mysql
CREATE TABLE roster_version ( CREATE TABLE roster_version (

View File

@ -41,7 +41,7 @@
list_users/2, list_users/2,
users_number/1, users_number/1,
users_number/2, users_number/2,
add_spool_sql/2, add_spool_sql/3,
add_spool/2, add_spool/2,
get_and_del_spool_msg_t/2, get_and_del_spool_msg_t/2,
del_spool_msg/2, del_spool_msg/2,
@ -52,27 +52,27 @@
get_roster_by_jid/3, get_roster_by_jid/3,
get_rostergroup_by_jid/3, get_rostergroup_by_jid/3,
del_roster/3, del_roster/3,
del_roster_sql/2, del_roster_sql/3,
update_roster/5, update_roster/5,
update_roster_sql/4, update_roster_sql/5,
roster_subscribe/4, roster_subscribe/4,
get_subscription/3, get_subscription/3,
set_private_data/4, set_private_data/4,
set_private_data_sql/3, set_private_data_sql/4,
get_private_data/3, get_private_data/3,
del_user_private_storage/2, del_user_private_storage/2,
get_default_privacy_list/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/2,
get_privacy_list_names_t/1, get_privacy_list_names_t/2,
get_privacy_list_id/3, 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/3,
get_privacy_list_data_by_id/2, get_privacy_list_data_by_id/2,
set_default_privacy_list/2, set_default_privacy_list/3,
unset_default_privacy_list/2, unset_default_privacy_list/2,
remove_privacy_list/2, remove_privacy_list/3,
add_privacy_list/2, add_privacy_list/3,
set_privacy_list/2, set_privacy_list/2,
del_privacy_lists/3, del_privacy_lists/3,
set_vcard/26, set_vcard/26,
@ -122,65 +122,75 @@ sql_transaction(LServer, F) ->
ejabberd_odbc:sql_transaction(LServer, F). ejabberd_odbc:sql_transaction(LServer, F).
get_last(LServer, Username) -> get_last(LServer, Username) ->
Host = escape(LServer),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["select seconds, state from last " ["select seconds, state from last "
"where username='", Username, "'"]). "where username='", Username, "' and host='", Host, "'"]).
set_last_t(LServer, Username, Seconds, State) -> set_last_t(LServer, Username, Seconds, State) ->
Host = escape(LServer),
%% MREMOND: I think this should be turn into a non transactional behaviour %% MREMOND: I think this should be turn into a non transactional behaviour
ejabberd_odbc:sql_transaction( ejabberd_odbc:sql_transaction(
LServer, LServer,
fun() -> fun() ->
update_t("last", ["username", "seconds", "state"], update_t("last", ["username", "host", "seconds", "state"],
[Username, Seconds, State], [Username, Host, Seconds, State],
["username='", Username, "'"]) ["username='", Username, "' and host='", Host, "'"])
end). end).
del_last(LServer, Username) -> del_last(LServer, Username) ->
Host = escape(LServer),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["delete from last where username='", Username, "'"]). ["delete from last where username='", Username, "' and host='", Host, "'"]).
get_password(LServer, Username) -> get_password(LServer, Username) ->
Host = escape(LServer),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["select password from users " ["select password from users "
"where username='", Username, "';"]). "where username='", Username, "' and host='", Host, "';"]).
set_password_t(LServer, Username, Pass) -> set_password_t(LServer, Username, Pass) ->
Host = escape(LServer),
ejabberd_odbc:sql_transaction( ejabberd_odbc:sql_transaction(
LServer, LServer,
fun() -> fun() ->
update_t("users", ["username", "password"], update_t("users", ["username", "host", "password"],
[Username, Pass], [Username, Host, Pass],
["username='", Username ,"'"]) ["username='", Username ,"' and host='", Host, "'"])
end). end).
add_user(LServer, Username, Pass) -> add_user(LServer, Username, Pass) ->
Host = escape(LServer),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["insert into users(username, password) " ["insert into users(username, host, password) "
"values ('", Username, "', '", Pass, "');"]). "values ('", Username, "', '", Host, "', '", Pass, "');"]).
del_user(LServer, Username) -> del_user(LServer, Username) ->
Host = escape(LServer),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["delete from users where username='", Username ,"';"]). ["delete from users where username='", Username ,"' and host='", Host, "';"]).
del_user_return_password(_LServer, Username, Pass) -> del_user_return_password(LServer, Username, Pass) ->
P = ejabberd_odbc:sql_query_t( Host = escape(LServer),
["select password from users where username='", ejabberd_odbc:sql_query_t(
Username, "';"]), ["select password from users where username='",
Username, "' and host='", Host, "';"]),
ejabberd_odbc:sql_query_t(["delete from users " ejabberd_odbc:sql_query_t(["delete from users "
"where username='", Username, "where username='", Username,
"' and host='", Host,
"' and password='", Pass, "';"]), "' and password='", Pass, "';"]),
P. Pass. % REVIEWME
list_users(LServer) -> list_users(LServer) ->
Host = escape(LServer),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
"select username from users"). ["select username from users where host='", Host, "'"]).
list_users(LServer, [{from, Start}, {to, End}]) when is_integer(Start) and list_users(LServer, [{from, Start}, {to, End}]) when is_integer(Start) and
is_integer(End) -> is_integer(End) ->
@ -211,6 +221,7 @@ list_users(LServer, [{prefix, Prefix},
"limit ~w offset ~w ", [Prefix, Limit, Offset])). "limit ~w offset ~w ", [Prefix, Limit, Offset])).
users_number(LServer) -> users_number(LServer) ->
Host = escape(LServer),
case element(1, ejabberd_config:get_local_option({odbc_server, LServer})) of case element(1, ejabberd_config:get_local_option({odbc_server, LServer})) of
mysql -> mysql ->
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
@ -224,13 +235,13 @@ users_number(LServer) ->
"select reltuples from pg_class where oid = 'users'::regclass::oid"); "select reltuples from pg_class where oid = 'users'::regclass::oid");
_ -> _ ->
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
"select count(*) from users") ["select count(*) from users where host='", Host, "'"])
end; end;
_ -> _ ->
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
"select count(*) from users") ["select count(*) from users where host='", Host, "'"])
end. end.
users_number(LServer, [{prefix, Prefix}]) when is_list(Prefix) -> 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, []) ->
users_number(LServer). users_number(LServer).
add_spool_sql(LServer, Username, XML) ->
add_spool_sql(Username, XML) -> Host = escape(LServer),
["insert into spool(username, xml) " ["insert into spool(username, host, xml) "
"values ('", Username, "', '", "values ('", Username, "', '", Host, "', '",
XML, XML,
"');"]. "');"].
@ -255,158 +266,177 @@ add_spool(LServer, Queries) ->
LServer, Queries). LServer, Queries).
get_and_del_spool_msg_t(LServer, Username) -> get_and_del_spool_msg_t(LServer, Username) ->
Host = escape(LServer),
F = fun() -> F = fun() ->
Result = ejabberd_odbc:sql_query_t( Result = ejabberd_odbc:sql_query_t(
["select username, xml from spool where username='", Username, "'" ["select username, xml from spool where username='", Username, "'"
" and host='", Host, "'"
" order by seq;"]), " order by seq;"]),
ejabberd_odbc:sql_query_t( ejabberd_odbc:sql_query_t(
["delete from spool where username='", Username, "';"]), ["delete from spool where username='", Username, "' and host='", Host, "';"]),
Result Result
end, end,
ejabberd_odbc:sql_transaction(LServer,F). ejabberd_odbc:sql_transaction(LServer,F).
del_spool_msg(LServer, Username) -> del_spool_msg(LServer, Username) ->
Host = escape(LServer),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["delete from spool where username='", Username, "';"]). ["delete from spool where username='", Username, "' and host='", Host, "';"]).
get_roster(LServer, Username) -> get_roster(LServer, Username) ->
Host = escape(LServer),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["select username, jid, nick, subscription, ask, " ["select username, jid, nick, subscription, ask, "
"askmessage, server, subscribe, type from rosterusers " "askmessage, server, subscribe, type from rosterusers "
"where username='", Username, "'"]). "where username='", Username, "' and host='", Host, "'"]).
get_roster_jid_groups(LServer, Username) -> get_roster_jid_groups(LServer, Username) ->
Host = escape(LServer),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["select jid, grp from rostergroups " ["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( ejabberd_odbc:sql_query_t(
["select grp from rostergroups " ["select grp from rostergroups "
"where username='", Username, "' " "where username='", Username, "' and host='", Host, "' "
"and jid='", SJID, "';"]). "and jid='", SJID, "';"]).
del_user_roster_t(LServer, Username) -> del_user_roster_t(LServer, Username) ->
Host = escape(LServer),
ejabberd_odbc:sql_transaction( ejabberd_odbc:sql_transaction(
LServer, LServer,
fun() -> fun() ->
ejabberd_odbc:sql_query_t( ejabberd_odbc:sql_query_t(
["delete from rosterusers " ["delete from rosterusers "
" where username='", Username, "';"]), " where username='", Username, "' and host='", Host, "';"]),
ejabberd_odbc:sql_query_t( ejabberd_odbc:sql_query_t(
["delete from rostergroups " ["delete from rostergroups "
" where username='", Username, "';"]) " where username='", Username, "' and host='", Host, "';"])
end). end).
get_roster_by_jid(_LServer, Username, SJID) -> get_roster_by_jid(LServer, Username, SJID) ->
Host = escape(LServer),
ejabberd_odbc:sql_query_t( ejabberd_odbc:sql_query_t(
["select username, jid, nick, subscription, " ["select username, jid, nick, subscription, "
"ask, askmessage, server, subscribe, type from rosterusers " "ask, askmessage, server, subscribe, type from rosterusers "
"where username='", Username, "' " "where username='", Username, "' and host='", Host, "' "
"and jid='", SJID, "';"]). "and jid='", SJID, "';"]).
get_rostergroup_by_jid(LServer, Username, SJID) -> get_rostergroup_by_jid(LServer, Username, SJID) ->
Host = escape(LServer),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["select grp from rostergroups " ["select grp from rostergroups "
"where username='", Username, "' " "where username='", Username, "' and host='", Host, "' "
"and jid='", SJID, "'"]). "and jid='", SJID, "'"]).
del_roster(_LServer, Username, SJID) -> del_roster(LServer, Username, SJID) ->
Host = escape(LServer),
ejabberd_odbc:sql_query_t( ejabberd_odbc:sql_query_t(
["delete from rosterusers " ["delete from rosterusers "
" where username='", Username, "' " " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"]), " and jid='", SJID, "';"]),
ejabberd_odbc:sql_query_t( ejabberd_odbc:sql_query_t(
["delete from rostergroups " ["delete from rostergroups "
" where username='", Username, "' " " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"]). " and jid='", SJID, "';"]).
del_roster_sql(Username, SJID) -> del_roster_sql(LServer, Username, SJID) ->
Host = escape(LServer),
[["delete from rosterusers " [["delete from rosterusers "
" where username='", Username, "' " " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"], " and jid='", SJID, "';"],
["delete from rostergroups " ["delete from rostergroups "
" where username='", Username, "' " " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"]]. " and jid='", SJID, "';"]].
update_roster(_LServer, Username, SJID, ItemVals, ItemGroups) -> update_roster(LServer, Username, SJID, ItemVals, ItemGroups) ->
Host = escape(LServer),
update_t("rosterusers", update_t("rosterusers",
["username", "jid", "nick", "subscription", "ask", ["host", "username", "jid", "nick", "subscription", "ask",
"askmessage", "server", "subscribe", "type"], "askmessage", "server", "subscribe", "type"],
ItemVals, [Host | ItemVals],
["username='", Username, "' and jid='", SJID, "'"]), ["username='", Username, "' and host='", Host, "' and jid='", SJID, "'"]),
ejabberd_odbc:sql_query_t( ejabberd_odbc:sql_query_t(
["delete from rostergroups " ["delete from rostergroups "
" where username='", Username, "' " " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"]), " and jid='", SJID, "';"]),
lists:foreach(fun(ItemGroup) -> lists:foreach(fun(ItemGroup) ->
ejabberd_odbc:sql_query_t( ejabberd_odbc:sql_query_t(
["insert into rostergroups(" ["insert into rostergroups("
" username, jid, grp) " " host, username, jid, grp) "
" values ('", string:join(ItemGroup, "', '"), "');"]) " values ('", Host, "', '", string:join(ItemGroup, "', '"), "');"])
end, end,
ItemGroups). ItemGroups).
update_roster_sql(Username, SJID, ItemVals, ItemGroups) -> update_roster_sql(LServer, Username, SJID, ItemVals, ItemGroups) ->
Host = escape(LServer),
[["delete from rosterusers " [["delete from rosterusers "
" where username='", Username, "' " " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"], " and jid='", SJID, "';"],
["insert into rosterusers(" ["insert into rosterusers("
" username, jid, nick, " " host, username, jid, nick, "
" subscription, ask, askmessage, " " subscription, ask, askmessage, "
" server, subscribe, type) " " server, subscribe, type) "
" values ('", string:join(ItemVals, "', '"), "');"], " values ('", Host, "', '", string:join(ItemVals, "', '"), "');"],
["delete from rostergroups " ["delete from rostergroups "
" where username='", Username, "' " " where username='", Username, "' and host='", Host, "' "
" and jid='", SJID, "';"]] ++ " and jid='", SJID, "';"]] ++
[["insert into rostergroups(" [["insert into rostergroups("
" username, jid, grp) " " host, username, jid, grp) "
" values ('", string:join(ItemGroup, "', '"), "');"] || " values ('", Host, "', '", string:join(ItemGroup, "', '"), "');"] ||
ItemGroup <- ItemGroups]. ItemGroup <- ItemGroups].
roster_subscribe(_LServer, Username, SJID, ItemVals) -> roster_subscribe(LServer, Username, SJID, ItemVals) ->
Host = escape(LServer),
update_t("rosterusers", update_t("rosterusers",
["username", "jid", "nick", "subscription", "ask", ["host", "username", "jid", "nick", "subscription", "ask",
"askmessage", "server", "subscribe", "type"], "askmessage", "server", "subscribe", "type"],
ItemVals, [Host | ItemVals],
["username='", Username, "' and jid='", SJID, "'"]). ["username='", Username, "' and host='", Host, "' and jid='", SJID, "'"]).
get_subscription(LServer, Username, SJID) -> get_subscription(LServer, Username, SJID) ->
Host = escape(LServer),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["select subscription from rosterusers " ["select subscription from rosterusers "
"where username='", Username, "' " "where username='", Username, "' and host='", Host, "' "
"and jid='", SJID, "'"]). "and jid='", SJID, "'"]).
set_private_data(_LServer, Username, LXMLNS, SData) -> set_private_data(LServer, Username, LXMLNS, SData) ->
Host = escape(LServer),
update_t("private_storage", update_t("private_storage",
["username", "namespace", "data"], ["host", "username", "namespace", "data"],
[Username, LXMLNS, SData], [Host, Username, LXMLNS, SData],
["username='", Username, "' and namespace='", LXMLNS, "'"]). ["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 " [["delete from private_storage "
"where username='", Username, "' and " "where username='", Username, "' and host='", Host, "' and "
"namespace='", LXMLNS, "';"], "namespace='", LXMLNS, "';"],
["insert into private_storage(username, namespace, data) " ["insert into private_storage(host, username, namespace, data) "
"values ('", Username, "', '", LXMLNS, "', " "values ('", Host, "', '", Username, "', '", LXMLNS, "', "
"'", SData, "');"]]. "'", SData, "');"]].
get_private_data(LServer, Username, LXMLNS) -> get_private_data(LServer, Username, LXMLNS) ->
Host = escape(LServer),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["select data from private_storage " ["select data from private_storage "
"where username='", Username, "' and " "where username='", Username, "' and host='", Host, "' and "
"namespace='", LXMLNS, "';"]). "namespace='", LXMLNS, "';"]).
del_user_private_storage(LServer, Username) -> del_user_private_storage(LServer, Username) ->
Host = escape(LServer),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, 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, set_vcard(LServer, LUsername, SBDay, SCTRY, SEMail, SFN, SFamily, SGiven,
SLBDay, SLCTRY, SLEMail, SLFN, SLFamily, SLGiven, SLLocality, 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( ejabberd_odbc:sql_transaction(
LServer, LServer,
fun() -> fun() ->
update_t("vcard", ["username", "vcard"], update_t("vcard", ["host", "username", "vcard"],
[LUsername, SVCARD], [LServer, LUsername, SVCARD],
["username='", LUsername, "'"]), ["username='", LUsername, "' and host='", LServer, "'"]),
update_t("vcard_search", update_t("vcard_search",
["username", "lusername", "fn", "lfn", "family", ["host", "username", "lusername", "fn", "lfn", "family",
"lfamily", "given", "lgiven", "middle", "lmiddle", "lfamily", "given", "lgiven", "middle", "lmiddle",
"nickname", "lnickname", "bday", "lbday", "ctry", "nickname", "lnickname", "bday", "lbday", "ctry",
"lctry", "locality", "llocality", "email", "lemail", "lctry", "locality", "llocality", "email", "lemail",
"orgname", "lorgname", "orgunit", "lorgunit"], "orgname", "lorgname", "orgunit", "lorgunit"],
[Username, LUsername, SFN, SLFN, SFamily, SLFamily, [LServer, Username, LUsername, SFN, SLFN, SFamily, SLFamily,
SGiven, SLGiven, SMiddle, SLMiddle, SNickname, SGiven, SLGiven, SMiddle, SLMiddle, SNickname,
SLNickname, SBDay, SLBDay, SCTRY, SLCTRY, SLNickname, SBDay, SLBDay, SCTRY, SLCTRY,
SLocality, SLLocality, SEMail, SLEMail, SOrgName, SLocality, SLLocality, SEMail, SLEMail, SOrgName,
@ -436,7 +466,7 @@ get_vcard(LServer, Username) ->
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["select vcard from vcard " ["select vcard from vcard "
"where username='", Username, "';"]). "where username='", Username, "' and host='", LServer, "';"]).
del_vcard(LServer, Username) -> del_vcard(LServer, Username) ->
ejabberd_odbc:sql_transaction( ejabberd_odbc:sql_transaction(
@ -457,34 +487,34 @@ get_default_privacy_list(LServer, Username) ->
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["select name from privacy_default_list " ["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( ejabberd_odbc:sql_query_t(
["select name from privacy_default_list " ["select name from privacy_default_list "
"where username='", Username, "';"]). "where username='", Username, "' and host='", LServer, "';"]).
get_privacy_list_names(LServer, Username) -> get_privacy_list_names(LServer, Username) ->
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["select name from privacy_list " ["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( ejabberd_odbc:sql_query_t(
["select name from privacy_list " ["select name from privacy_list "
"where username='", Username, "';"]). "where username='", Username, "' and host='", LServer, "';"]).
get_privacy_list_id(LServer, Username, SName) -> get_privacy_list_id(LServer, Username, SName) ->
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["select id from privacy_list " ["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( ejabberd_odbc:sql_query_t(
["select id from privacy_list " ["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) -> get_privacy_list_data(LServer, Username, SName) ->
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
@ -493,7 +523,7 @@ get_privacy_list_data(LServer, Username, SName) ->
"match_message, match_presence_in, match_presence_out " "match_message, match_presence_in, match_presence_out "
"from privacy_list_data " "from privacy_list_data "
"where id = (select id from privacy_list where " "where id = (select id from privacy_list where "
" username='", Username, "' and name='", SName, "') " " username='", Username, "' and name='", SName, "' and host='", LServer, "') "
"order by ord;"]). "order by ord;"]).
get_privacy_list_data_by_id(LServer, ID) -> get_privacy_list_data_by_id(LServer, ID) ->
@ -504,25 +534,26 @@ get_privacy_list_data_by_id(LServer, ID) ->
"from privacy_list_data " "from privacy_list_data "
"where id='", ID, "' order by ord;"]). "where id='", ID, "' order by ord;"]).
set_default_privacy_list(Username, SName) -> set_default_privacy_list(LServer, Username, SName) ->
update_t("privacy_default_list", ["username", "name"], update_t("privacy_default_list", ["host", "username", "name"],
[Username, SName], ["username='", Username, "'"]). [LServer, Username, SName],
["host='", LServer, "' and username='", Username, "'"]).
unset_default_privacy_list(LServer, Username) -> unset_default_privacy_list(LServer, Username) ->
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["delete from privacy_default_list " ["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( ejabberd_odbc:sql_query_t(
["delete from privacy_list " ["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( ejabberd_odbc:sql_query_t(
["insert into privacy_list(username, name) " ["insert into privacy_list(host, username, name) "
"values ('", Username, "', '", SName, "');"]). "values ('", LServer, "', '", Username, "', '", SName, "');"]).
set_privacy_list(ID, RItems) -> set_privacy_list(ID, RItems) ->
ejabberd_odbc:sql_query_t( ejabberd_odbc:sql_query_t(
@ -542,13 +573,13 @@ set_privacy_list(ID, RItems) ->
del_privacy_lists(LServer, Server, Username) -> del_privacy_lists(LServer, Server, Username) ->
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["delete from privacy_list where username='", Username, "';"]), ["delete from privacy_list where username='", Username, "' and host='", LServer, "';"]),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["delete from privacy_list_data where value='", Username++"@"++Server, "';"]), ["delete from privacy_list_data where value='", Username++"@"++Server, "';"]),
ejabberd_odbc:sql_query( ejabberd_odbc:sql_query(
LServer, LServer,
["delete from privacy_default_list where username='", Username, "';"]). ["delete from privacy_default_list where username='", Username, "' and host='", LServer, "';"]).
%% Characters to escape %% Characters to escape
escape($\0) -> "\\0"; escape($\0) -> "\\0";

View File

@ -18,21 +18,24 @@
-- --
CREATE TABLE users ( CREATE TABLE users (
username text PRIMARY KEY, username text NOT NULL,
host text NOT NULL,
"password" text NOT NULL, "password" text NOT NULL,
PRIMARY KEY (host, username)
created_at TIMESTAMP NOT NULL DEFAULT now() created_at TIMESTAMP NOT NULL DEFAULT now()
); );
CREATE TABLE last ( CREATE TABLE last (
username text PRIMARY KEY, username text NOT NULL,
host text NOT NULL,
seconds text NOT NULL, seconds text NOT NULL,
state text NOT NULL state text NOT NULL,
PRIMARY KEY (host, username)
); );
CREATE TABLE rosterusers ( CREATE TABLE rosterusers (
username text NOT NULL, username text NOT NULL,
host text NOT NULL,
jid text NOT NULL, jid text NOT NULL,
nick text NOT NULL, nick text NOT NULL,
subscription character(1) NOT NULL, subscription character(1) NOT NULL,
@ -41,42 +44,50 @@ CREATE TABLE rosterusers (
server character(1) NOT NULL, server character(1) NOT NULL,
subscribe text, subscribe text,
"type" text, "type" text,
<<<<<<< HEAD
PRIMARY KEY (host, username, jid)
=======
created_at TIMESTAMP NOT NULL DEFAULT now() 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 ( CREATE TABLE rostergroups (
username text NOT NULL, username text NOT NULL,
host text NOT NULL,
jid 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 ( CREATE TABLE spool (
username text NOT NULL, username text NOT NULL,
host text NOT NULL,
xml text NOT NULL, xml text NOT NULL,
seq SERIAL, seq SERIAL,
<<<<<<< HEAD
PRIMARY KEY (host, username, seq)
=======
created_at TIMESTAMP NOT NULL DEFAULT now() created_at TIMESTAMP NOT NULL DEFAULT now()
>>>>>>> 30
); );
CREATE INDEX i_despool ON spool USING btree (username);
CREATE TABLE vcard ( CREATE TABLE vcard (
<<<<<<< HEAD
username text NOT NULL,
host text NOT NULL,
vcard text NOT NULL,
PRIMARY KEY (host, username)
=======
username text PRIMARY KEY, username text PRIMARY KEY,
vcard text NOT NULL, vcard text NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT now() created_at TIMESTAMP NOT NULL DEFAULT now()
>>>>>>> 30
); );
CREATE TABLE vcard_search ( CREATE TABLE vcard_search (
host text NOT NULL,
username text NOT NULL, username text NOT NULL,
lusername text PRIMARY KEY, lusername text NOT NULL,
fn text NOT NULL, fn text NOT NULL,
lfn text NOT NULL, lfn text NOT NULL,
family text NOT NULL, family text NOT NULL,
@ -98,9 +109,11 @@ CREATE TABLE vcard_search (
orgname text NOT NULL, orgname text NOT NULL,
lorgname text NOT NULL, lorgname text NOT NULL,
orgunit 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_lfn ON vcard_search(lfn);
CREATE INDEX i_vcard_search_lfamily ON vcard_search(lfamily); CREATE INDEX i_vcard_search_lfamily ON vcard_search(lfamily);
CREATE INDEX i_vcard_search_lgiven ON vcard_search(lgiven); 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 INDEX i_vcard_search_lorgunit ON vcard_search(lorgunit);
CREATE TABLE privacy_default_list ( CREATE TABLE privacy_default_list (
username text PRIMARY KEY, username text NOT NULL,
name text NOT NULL host text NOT NULL,
name text NOT NULL,
PRIMARY KEY (host, username)
); );
CREATE TABLE privacy_list ( CREATE TABLE privacy_list (
username text NOT NULL, username text NOT NULL,
host text NOT NULL,
name text NOT NULL, name text NOT NULL,
id SERIAL UNIQUE, id SERIAL UNIQUE,
<<<<<<< HEAD
PRIMARY KEY (host, username, name)
=======
created_at TIMESTAMP NOT NULL DEFAULT now() 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 ( CREATE TABLE privacy_list_data (
id bigint REFERENCES privacy_list(id) ON DELETE CASCADE, id bigint REFERENCES privacy_list(id) ON DELETE CASCADE,
t character(1) NOT NULL, t character(1) NOT NULL,
@ -138,18 +155,28 @@ CREATE TABLE privacy_list_data (
match_iq boolean NOT NULL, match_iq boolean NOT NULL,
match_message boolean NOT NULL, match_message boolean NOT NULL,
match_presence_in 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 ( CREATE TABLE private_storage (
username text NOT NULL, username text NOT NULL,
host text NOT NULL,
namespace text NOT NULL, namespace text NOT NULL,
data text NOT NULL, data text NOT NULL,
<<<<<<< HEAD
PRIMARY KEY (host, username, namespace)
=======
created_at TIMESTAMP NOT NULL DEFAULT now() created_at TIMESTAMP NOT NULL DEFAULT now()
>>>>>>> 30
); );
CREATE INDEX i_private_storage_username ON private_storage USING btree (username); CREATE TABLE hosts (
CREATE UNIQUE INDEX i_private_storage_username_namespace ON private_storage USING btree (username, namespace); clusterid integer NOT NULL,
host text NOT NULL,
config text NOT NULL,
PRIMARY KEY (host)
);
CREATE TABLE roster_version ( CREATE TABLE roster_version (

View File

@ -33,7 +33,12 @@
start() -> 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() -> init() ->
{A1, A2, A3} = now(), {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) -> process(["server", SHost | RPath] = Path, #request{auth = Auth, lang = Lang, host = HostHTTP, method = Method} = Request) ->
Host = exmpp_stringprep:nameprep(SHost), Host = exmpp_stringprep:nameprep(SHost),
case lists:member(Host, ?MYHOSTS) of case ?IS_MY_HOST(Host) of
true -> true ->
case get_auth_admin(Auth, HostHTTP, Path, Method) of case get_auth_admin(Auth, HostHTTP, Path, Method) of
{ok, {User, Server}} -> {ok, {User, Server}} ->