2003-01-07 20:10:35 +01:00
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
%%% File : ejabberd_config.erl
|
|
|
|
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
2005-06-20 05:18:13 +02:00
|
|
|
%%% Purpose : Load config file
|
2003-01-07 20:10:35 +01:00
|
|
|
%%% Created : 14 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
|
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
|
|
|
|
-module(ejabberd_config).
|
|
|
|
-author('alexey@sevcom.net').
|
|
|
|
|
2003-01-21 21:36:55 +01:00
|
|
|
-export([start/0, load_file/1,
|
|
|
|
add_global_option/2, add_local_option/2,
|
|
|
|
get_global_option/1, get_local_option/1]).
|
2003-01-07 20:10:35 +01:00
|
|
|
|
|
|
|
-include("ejabberd.hrl").
|
2007-11-09 11:18:38 +01:00
|
|
|
-include("ejabberd_config.hrl").
|
2003-01-21 21:36:55 +01:00
|
|
|
|
2003-01-07 20:10:35 +01:00
|
|
|
start() ->
|
2003-01-21 21:36:55 +01:00
|
|
|
mnesia:create_table(config,
|
|
|
|
[{disc_copies, [node()]},
|
|
|
|
{attributes, record_info(fields, config)}]),
|
|
|
|
mnesia:add_table_copy(config, node(), ram_copies),
|
|
|
|
mnesia:create_table(local_config,
|
|
|
|
[{disc_copies, [node()]},
|
|
|
|
{local_content, true},
|
|
|
|
{attributes, record_info(fields, local_config)}]),
|
|
|
|
mnesia:add_table_copy(local_config, node(), ram_copies),
|
2003-10-12 20:21:16 +02:00
|
|
|
Config = case application:get_env(config) of
|
|
|
|
{ok, Path} -> Path;
|
2004-09-25 22:52:20 +02:00
|
|
|
undefined ->
|
|
|
|
case os:getenv("EJABBERD_CONFIG_PATH") of
|
|
|
|
false ->
|
|
|
|
?CONFIG_PATH;
|
|
|
|
Path ->
|
|
|
|
Path
|
|
|
|
end
|
2003-10-12 20:21:16 +02:00
|
|
|
end,
|
|
|
|
load_file(Config).
|
2003-01-07 20:10:35 +01:00
|
|
|
|
|
|
|
|
|
|
|
load_file(File) ->
|
2003-01-16 21:24:53 +01:00
|
|
|
case file:consult(File) of
|
|
|
|
{ok, Terms} ->
|
2005-06-20 05:18:13 +02:00
|
|
|
State = lists:foldl(fun search_hosts/2, #state{}, Terms),
|
|
|
|
Res = lists:foldl(fun process_term/2, State, Terms),
|
2003-02-01 21:21:28 +01:00
|
|
|
set_opts(Res);
|
2003-01-16 21:24:53 +01:00
|
|
|
{error, Reason} ->
|
2003-10-12 20:21:16 +02:00
|
|
|
?ERROR_MSG("Can't load config file ~p: ~p", [File, Reason]),
|
2007-11-22 14:46:54 +01:00
|
|
|
exit(File ++ ": " ++ file:format_error(Reason))
|
2003-01-07 20:10:35 +01:00
|
|
|
end.
|
|
|
|
|
2005-06-20 05:18:13 +02:00
|
|
|
search_hosts(Term, State) ->
|
|
|
|
case Term of
|
|
|
|
{host, Host} ->
|
|
|
|
if
|
|
|
|
State#state.hosts == [] ->
|
2007-06-29 19:27:42 +02:00
|
|
|
add_hosts_to_option([Host], State);
|
2005-06-20 05:18:13 +02:00
|
|
|
true ->
|
|
|
|
?ERROR_MSG("Can't load config file: "
|
|
|
|
"too many hosts definitions", []),
|
|
|
|
exit("too many hosts definitions")
|
|
|
|
end;
|
|
|
|
{hosts, Hosts} ->
|
|
|
|
if
|
|
|
|
State#state.hosts == [] ->
|
2007-06-29 19:27:42 +02:00
|
|
|
add_hosts_to_option(Hosts, State);
|
2005-06-20 05:18:13 +02:00
|
|
|
true ->
|
|
|
|
?ERROR_MSG("Can't load config file: "
|
|
|
|
"too many hosts definitions", []),
|
|
|
|
exit("too many hosts definitions")
|
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
State
|
|
|
|
end.
|
|
|
|
|
2007-06-29 19:27:42 +02:00
|
|
|
add_hosts_to_option(Hosts, State) ->
|
|
|
|
PrepHosts = normalize_hosts(Hosts),
|
|
|
|
add_option(hosts, PrepHosts, State#state{hosts = PrepHosts}).
|
|
|
|
|
|
|
|
normalize_hosts(Hosts) ->
|
|
|
|
normalize_hosts(Hosts,[]).
|
|
|
|
normalize_hosts([], PrepHosts) ->
|
|
|
|
lists:reverse(PrepHosts);
|
|
|
|
normalize_hosts([Host|Hosts], PrepHosts) ->
|
|
|
|
case jlib:nodeprep(Host) of
|
|
|
|
error ->
|
|
|
|
?ERROR_MSG("Can't load config file: "
|
|
|
|
"invalid host name [~p]", [Host]),
|
|
|
|
exit("invalid hostname");
|
|
|
|
PrepHost ->
|
|
|
|
normalize_hosts(Hosts, [PrepHost|PrepHosts])
|
|
|
|
end.
|
|
|
|
|
2003-02-01 21:21:28 +01:00
|
|
|
process_term(Term, State) ->
|
2003-01-18 20:42:48 +01:00
|
|
|
case Term of
|
2003-02-01 21:21:28 +01:00
|
|
|
override_global ->
|
2003-02-02 20:49:19 +01:00
|
|
|
State#state{override_global = true};
|
2003-02-01 21:21:28 +01:00
|
|
|
override_local ->
|
2003-02-02 20:49:19 +01:00
|
|
|
State#state{override_local = true};
|
2003-02-01 21:21:28 +01:00
|
|
|
override_acls ->
|
2003-02-02 20:49:19 +01:00
|
|
|
State#state{override_acls = true};
|
2007-12-07 00:15:04 +01:00
|
|
|
{acl, _ACLName, _ACLData} ->
|
2005-06-20 05:18:13 +02:00
|
|
|
process_host_term(Term, global, State);
|
2007-12-07 00:15:04 +01:00
|
|
|
{access, _RuleName, _Rules} ->
|
2005-06-20 05:18:13 +02:00
|
|
|
process_host_term(Term, global, State);
|
2007-12-07 00:15:04 +01:00
|
|
|
{shaper, _Name, _Data} ->
|
|
|
|
%%lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
|
|
|
|
%% State, State#state.hosts);
|
2006-10-02 20:54:10 +02:00
|
|
|
process_host_term(Term, global, State);
|
2007-12-07 00:15:04 +01:00
|
|
|
{host, _Host} ->
|
2005-06-20 05:18:13 +02:00
|
|
|
State;
|
2007-12-07 00:15:04 +01:00
|
|
|
{hosts, _Hosts} ->
|
2005-06-20 05:18:13 +02:00
|
|
|
State;
|
2005-07-31 21:51:52 +02:00
|
|
|
{host_config, Host, Terms} ->
|
|
|
|
lists:foldl(fun(T, S) -> process_host_term(T, Host, S) end,
|
|
|
|
State, Terms);
|
2005-07-03 02:16:34 +02:00
|
|
|
{listen, Val} ->
|
|
|
|
add_option(listen, Val, State);
|
2007-06-07 20:27:35 +02:00
|
|
|
{language, Val} ->
|
|
|
|
add_option(language, Val, State);
|
2005-07-13 05:24:13 +02:00
|
|
|
{outgoing_s2s_port, Port} ->
|
|
|
|
add_option(outgoing_s2s_port, Port, State);
|
2005-10-25 03:08:37 +02:00
|
|
|
{s2s_use_starttls, Port} ->
|
|
|
|
add_option(s2s_use_starttls, Port, State);
|
2005-11-05 22:15:53 +01:00
|
|
|
{s2s_certfile, CertFile} ->
|
|
|
|
add_option(s2s_certfile, CertFile, State);
|
|
|
|
{domain_certfile, Domain, CertFile} ->
|
|
|
|
add_option({domain_certfile, Domain}, CertFile, State);
|
2006-11-07 03:08:51 +01:00
|
|
|
{node_type, NodeType} ->
|
|
|
|
add_option(node_type, NodeType, State);
|
|
|
|
{cluster_nodes, Nodes} ->
|
|
|
|
add_option(cluster_nodes, Nodes, State);
|
|
|
|
{domain_balancing, Domain, Balancing} ->
|
|
|
|
add_option({domain_balancing, Domain}, Balancing, State);
|
2007-01-19 05:46:44 +01:00
|
|
|
{domain_balancing_component_number, Domain, N} ->
|
|
|
|
add_option({domain_balancing_component_number, Domain}, N, State);
|
2007-04-12 07:31:53 +02:00
|
|
|
{watchdog_admins, Admins} ->
|
|
|
|
add_option(watchdog_admins, Admins, State);
|
2007-01-19 05:46:44 +01:00
|
|
|
{loglevel, Loglevel} ->
|
|
|
|
ejabberd_loglevel:set(Loglevel),
|
|
|
|
State;
|
2007-12-07 00:15:04 +01:00
|
|
|
{_Opt, _Val} ->
|
2005-06-20 05:18:13 +02:00
|
|
|
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
|
|
|
|
State, State#state.hosts)
|
|
|
|
end.
|
|
|
|
|
|
|
|
process_host_term(Term, Host, State) ->
|
|
|
|
case Term of
|
2003-01-18 20:42:48 +01:00
|
|
|
{acl, ACLName, ACLData} ->
|
2003-02-02 20:49:19 +01:00
|
|
|
State#state{opts =
|
2007-12-07 00:15:04 +01:00
|
|
|
[acl:to_record(Host, ACLName, ACLData) | State#state.opts]};
|
2003-01-21 21:36:55 +01:00
|
|
|
{access, RuleName, Rules} ->
|
2005-06-20 05:18:13 +02:00
|
|
|
State#state{opts = [#config{key = {access, RuleName, Host},
|
2003-02-02 20:49:19 +01:00
|
|
|
value = Rules} |
|
|
|
|
State#state.opts]};
|
2003-02-09 20:17:23 +01:00
|
|
|
{shaper, Name, Data} ->
|
2005-06-20 05:18:13 +02:00
|
|
|
State#state{opts = [#config{key = {shaper, Name, Host},
|
2003-02-09 20:17:23 +01:00
|
|
|
value = Data} |
|
|
|
|
State#state.opts]};
|
2005-04-17 20:08:34 +02:00
|
|
|
{host, Host} ->
|
2005-06-20 05:18:13 +02:00
|
|
|
State;
|
2007-12-07 00:15:04 +01:00
|
|
|
{hosts, _Hosts} ->
|
2005-06-20 05:18:13 +02:00
|
|
|
State;
|
2007-11-26 20:52:09 +01:00
|
|
|
{odbc_server, ODBC_server} ->
|
|
|
|
odbc_modules_found = check_odbc_modules(ODBC_server),
|
|
|
|
add_option({odbc_server, Host}, ODBC_server, State);
|
2003-01-18 20:42:48 +01:00
|
|
|
{Opt, Val} ->
|
2005-06-20 05:18:13 +02:00
|
|
|
add_option({Opt, Host}, Val, State)
|
2003-01-21 21:36:55 +01:00
|
|
|
end.
|
|
|
|
|
2003-02-01 21:21:28 +01:00
|
|
|
add_option(Opt, Val, State) ->
|
2003-01-21 21:36:55 +01:00
|
|
|
Table = case Opt of
|
2005-04-17 20:08:34 +02:00
|
|
|
hosts ->
|
2003-01-21 21:36:55 +01:00
|
|
|
config;
|
2004-07-10 00:34:26 +02:00
|
|
|
language ->
|
|
|
|
config;
|
2003-01-21 21:36:55 +01:00
|
|
|
_ ->
|
|
|
|
local_config
|
|
|
|
end,
|
|
|
|
case Table of
|
|
|
|
config ->
|
2003-02-02 20:49:19 +01:00
|
|
|
State#state{opts = [#config{key = Opt, value = Val} |
|
|
|
|
State#state.opts]};
|
2003-01-21 21:36:55 +01:00
|
|
|
local_config ->
|
2007-07-31 06:13:29 +02:00
|
|
|
case Opt of
|
|
|
|
{{add, OptName}, Host} ->
|
|
|
|
State#state{opts = compact({OptName, Host}, Val,
|
|
|
|
State#state.opts, [])};
|
|
|
|
_ ->
|
|
|
|
State#state{opts = [#local_config{key = Opt, value = Val} |
|
|
|
|
State#state.opts]}
|
|
|
|
end
|
|
|
|
end.
|
|
|
|
|
|
|
|
compact(Opt, Val, [], Os) ->
|
|
|
|
[#local_config{key = Opt, value = Val}] ++ Os;
|
|
|
|
compact(Opt, Val, [O | Os1], Os2) ->
|
|
|
|
case O#local_config.key of
|
|
|
|
Opt ->
|
|
|
|
Os2 ++ [#local_config{key = Opt,
|
|
|
|
value = Val++O#local_config.value}
|
|
|
|
] ++ Os1;
|
|
|
|
_ ->
|
|
|
|
compact(Opt, Val, Os1, Os2++[O])
|
2003-01-18 20:42:48 +01:00
|
|
|
end.
|
|
|
|
|
2003-02-01 21:21:28 +01:00
|
|
|
|
|
|
|
set_opts(State) ->
|
|
|
|
Opts = lists:reverse(State#state.opts),
|
2003-02-02 20:49:19 +01:00
|
|
|
F = fun() ->
|
|
|
|
if
|
|
|
|
State#state.override_global ->
|
|
|
|
Ksg = mnesia:all_keys(config),
|
|
|
|
lists:foreach(fun(K) ->
|
|
|
|
mnesia:delete({config, K})
|
|
|
|
end, Ksg);
|
|
|
|
true ->
|
|
|
|
ok
|
|
|
|
end,
|
|
|
|
if
|
|
|
|
State#state.override_local ->
|
|
|
|
Ksl = mnesia:all_keys(local_config),
|
|
|
|
lists:foreach(fun(K) ->
|
|
|
|
mnesia:delete({local_config, K})
|
|
|
|
end, Ksl);
|
|
|
|
true ->
|
|
|
|
ok
|
|
|
|
end,
|
|
|
|
if
|
|
|
|
State#state.override_acls ->
|
|
|
|
Ksa = mnesia:all_keys(acl),
|
|
|
|
lists:foreach(fun(K) ->
|
|
|
|
mnesia:delete({acl, K})
|
|
|
|
end, Ksa);
|
|
|
|
true ->
|
|
|
|
ok
|
|
|
|
end,
|
|
|
|
lists:foreach(fun(R) ->
|
|
|
|
mnesia:write(R)
|
|
|
|
end, Opts)
|
|
|
|
end,
|
2007-12-08 19:39:23 +01:00
|
|
|
case mnesia:transaction(F) of
|
|
|
|
{atomic, _} -> ok;
|
|
|
|
{aborted,{no_exists,Table}} ->
|
|
|
|
MnesiaDirectory = mnesia:system_info(directory),
|
|
|
|
?ERROR_MSG("Error reading Mnesia database spool files:~n"
|
|
|
|
"The Mnesia database couldn't read the spool file for the table '~p'.~n"
|
|
|
|
"ejabberd needs read and write access in the directory:~n ~s~n"
|
|
|
|
"Maybe the problem is a change in the computer hostname,~n"
|
|
|
|
"or a change in the Erlang node name, which is currently:~n ~p~n"
|
|
|
|
"Check the ejabberd guide for details about changing the~n"
|
|
|
|
"computer hostname or Erlang node name.~n",
|
|
|
|
[Table, MnesiaDirectory, node()]),
|
|
|
|
exit("Error reading Mnesia database")
|
|
|
|
end.
|
2003-02-01 21:21:28 +01:00
|
|
|
|
|
|
|
|
2003-01-21 21:36:55 +01:00
|
|
|
add_global_option(Opt, Val) ->
|
|
|
|
mnesia:transaction(fun() ->
|
|
|
|
mnesia:write(#config{key = Opt,
|
|
|
|
value = Val})
|
|
|
|
end).
|
|
|
|
|
|
|
|
add_local_option(Opt, Val) ->
|
|
|
|
mnesia:transaction(fun() ->
|
|
|
|
mnesia:write(#local_config{key = Opt,
|
|
|
|
value = Val})
|
|
|
|
end).
|
|
|
|
|
|
|
|
|
|
|
|
get_global_option(Opt) ->
|
|
|
|
case ets:lookup(config, Opt) of
|
|
|
|
[#config{value = Val}] ->
|
|
|
|
Val;
|
|
|
|
_ ->
|
|
|
|
undefined
|
|
|
|
end.
|
2003-01-16 21:24:53 +01:00
|
|
|
|
2003-01-21 21:36:55 +01:00
|
|
|
get_local_option(Opt) ->
|
|
|
|
case ets:lookup(local_config, Opt) of
|
|
|
|
[#local_config{value = Val}] ->
|
2003-01-07 20:10:35 +01:00
|
|
|
Val;
|
|
|
|
_ ->
|
|
|
|
undefined
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
2007-11-26 20:52:09 +01:00
|
|
|
check_odbc_modules(ODBC_server) ->
|
|
|
|
case catch check_odbc_modules2(ODBC_server) of
|
|
|
|
{'EXIT', {undef, [{Module, module_info, []} | _]}} ->
|
|
|
|
?CRITICAL_MSG("ejabberd is configured to use ODBC, but the Erlang module '~p' is not installed.", [Module]),
|
|
|
|
odbc_module_not_found;
|
|
|
|
_ -> odbc_modules_found
|
|
|
|
end.
|
|
|
|
|
|
|
|
check_odbc_modules2(ODBC_server) ->
|
|
|
|
check_modules_exists([ejabberd_odbc, ejabberd_odbc_sup, odbc_queries]),
|
|
|
|
case ODBC_server of
|
|
|
|
{mysql, _Server, _DB, _Username, _Password} ->
|
|
|
|
check_modules_exists([mysql, mysql_auth, mysql_conn, mysql_recv]);
|
2007-12-07 00:15:04 +01:00
|
|
|
|
2007-11-26 20:52:09 +01:00
|
|
|
{mysql, _Server, _Port, _DB, _Username, _Password} ->
|
|
|
|
check_modules_exists([mysql, mysql_auth, mysql_conn, mysql_recv]);
|
2007-12-07 00:15:04 +01:00
|
|
|
|
2007-11-26 20:52:09 +01:00
|
|
|
{pgsql, _Server, _DB, _Username, _Password} ->
|
|
|
|
check_modules_exists([pgsql, pgsql_proto, pgsql_tcp, pgsql_util]);
|
2007-12-07 00:15:04 +01:00
|
|
|
|
2007-11-26 20:52:09 +01:00
|
|
|
{pgsql, _Server, _Port, _DB, _Username, _Password} ->
|
|
|
|
check_modules_exists([pgsql, pgsql_proto, pgsql_tcp, pgsql_util]);
|
2007-12-07 00:15:04 +01:00
|
|
|
|
2007-11-26 20:52:09 +01:00
|
|
|
Server when is_list(Server) ->
|
|
|
|
ok
|
|
|
|
end.
|
|
|
|
|
|
|
|
check_modules_exists(Modules) ->
|
|
|
|
[true = is_list(Module:module_info()) || Module <- Modules].
|