aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ejabberd_config.erl206
1 files changed, 193 insertions, 13 deletions
diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl
index 0222cc367..a585b39fb 100644
--- a/src/ejabberd_config.erl
+++ b/src/ejabberd_config.erl
@@ -35,6 +35,15 @@
-include("ejabberd.hrl").
-include("ejabberd_config.hrl").
+
+%% @type macro() = {macro_key(), macro_value()}
+
+%% @type macro_key() = atom().
+%% The atom must have all characters in uppercase.
+
+%% @type macro_value() = term().
+
+
start() ->
mnesia:create_table(config,
[{disc_copies, [node()]},
@@ -45,30 +54,67 @@ start() ->
{local_content, true},
{attributes, record_info(fields, local_config)}]),
mnesia:add_table_copy(local_config, node(), ram_copies),
- Config = case application:get_env(config) of
- {ok, Path} -> Path;
- undefined ->
- case os:getenv("EJABBERD_CONFIG_PATH") of
- false ->
- ?CONFIG_PATH;
- Path ->
- Path
- end
- end,
+ Config = get_ejabberd_config_path(),
load_file(Config).
+%% @doc Get the filename of the ejabberd configuration file.
+%% The filename can be specified with: erl -config "/path/to/ejabberd.cfg".
+%% It can also be specified with the environtment variable EJABBERD_CONFIG_PATH.
+%% If not specified, the default value 'ejabberd.cfg' is assumed.
+%% @spec () -> string()
+get_ejabberd_config_path() ->
+ case application:get_env(config) of
+ {ok, Path} -> Path;
+ undefined ->
+ case os:getenv("EJABBERD_CONFIG_PATH") of
+ false ->
+ ?CONFIG_PATH;
+ Path ->
+ Path
+ end
+ end.
+%% @doc Load the ejabberd configuration file.
+%% It also includes additional configuration files and replaces macros.
+%% @spec (File::string()) -> [term()]
load_file(File) ->
+ Terms = get_plain_terms_file(File),
+ State = lists:foldl(fun search_hosts/2, #state{}, Terms),
+ Terms_macros = replace_macros(Terms),
+ Res = lists:foldl(fun process_term/2, State, Terms_macros),
+ set_opts(Res).
+
+%% @doc Read an ejabberd configuration file and return the terms.
+%% Input is an absolute or relative path to an ejabberd config file.
+%% Returns a list of plain terms,
+%% in which the options 'include_config_file' were parsed
+%% and the terms in those files were included.
+%% @spec(string()) -> [term()]
+get_plain_terms_file(File1) ->
+ File = get_absolute_path(File1),
case file:consult(File) of
{ok, Terms} ->
- State = lists:foldl(fun search_hosts/2, #state{}, Terms),
- Res = lists:foldl(fun process_term/2, State, Terms),
- set_opts(Res);
+ include_config_files(Terms);
{error, Reason} ->
?ERROR_MSG("Can't load config file ~p: ~p", [File, Reason]),
exit(File ++ ": " ++ file:format_error(Reason))
end.
+%% @doc Convert configuration filename to absolute path.
+%% Input is an absolute or relative path to an ejabberd configuration file.
+%% And returns an absolute path to the configuration file.
+%% @spec (string()) -> string()
+get_absolute_path(File) ->
+ case filename:pathtype(File) of
+ absolute ->
+ File;
+ relative ->
+ Config_path = get_ejabberd_config_path(),
+ Config_dir = filename:dirname(Config_path),
+ filename:absname_join(Config_dir, File)
+ end.
+
+
search_hosts(Term, State) ->
case Term of
{host, Host} ->
@@ -111,6 +157,140 @@ normalize_hosts([Host|Hosts], PrepHosts) ->
normalize_hosts(Hosts, [PrepHost|PrepHosts])
end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Support for 'include_config_file'
+
+%% @doc Include additional configuration files in the list of terms.
+%% @spec ([term()]) -> [term()]
+include_config_files(Terms) ->
+ include_config_files(Terms, []).
+
+include_config_files([], Res) ->
+ Res;
+include_config_files([{include_config_file, Filename} | Terms], Res) ->
+ include_config_files([{include_config_file, Filename, []} | Terms], Res);
+include_config_files([{include_config_file, Filename, Options} | Terms], Res) ->
+ Included_terms = get_plain_terms_file(Filename),
+ Disallow = proplists:get_value(disallow, Options, []),
+ Included_terms2 = delete_disallowed(Disallow, Included_terms),
+ Allow_only = proplists:get_value(allow_only, Options, all),
+ Included_terms3 = keep_only_allowed(Allow_only, Included_terms2),
+ include_config_files(Terms, Res ++ Included_terms3);
+include_config_files([Term | Terms], Res) ->
+ include_config_files(Terms, Res ++ [Term]).
+
+%% @doc Filter from the list of terms the disallowed.
+%% Returns a sublist of Terms without the ones which first element is
+%% included in Disallowed.
+%% @spec (Disallowed::[atom()], Terms::[term()]) -> [term()]
+delete_disallowed(Disallowed, Terms) ->
+ lists:foldl(
+ fun(Dis, Ldis) ->
+ delete_disallowed2(Dis, Ldis)
+ end,
+ Terms,
+ Disallowed).
+
+delete_disallowed2(Disallowed, [H|T]) ->
+ case element(1, H) of
+ Disallowed ->
+ ?WARNING_MSG("The option '~p' is disallowed, "
+ "and will not be accepted", [Disallowed]),
+ delete_disallowed2(Disallowed, T);
+ _ ->
+ [H|delete_disallowed2(Disallowed, T)]
+ end;
+delete_disallowed2(_, []) ->
+ [].
+
+%% @doc Keep from the list only the allowed terms.
+%% Returns a sublist of Terms with only the ones which first element is
+%% included in Allowed.
+%% @spec (Allowed::[atom()], Terms::[term()]) -> [term()]
+keep_only_allowed(all, Terms) ->
+ Terms;
+keep_only_allowed(Allowed, Terms) ->
+ {As, NAs} = lists:partition(
+ fun(Term) ->
+ lists:member(element(1, Term), Allowed)
+ end,
+ Terms),
+ [?WARNING_MSG("This option is not allowed, "
+ "and will not be accepted:~n~p", [NA])
+ || NA <- NAs],
+ As.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Support for Macro
+
+%% @doc Replace the macros with their defined values.
+%% @spec (Terms::[term()]) -> [term()]
+replace_macros(Terms) ->
+ {TermsOthers, Macros} = split_terms_macros(Terms),
+ replace(TermsOthers, Macros).
+
+%% @doc Split Terms into normal terms and macro definitions.
+%% @spec (Terms) -> {Terms, Macros}
+%% Terms = [term()]
+%% Macros = [macro()]
+split_terms_macros(Terms) ->
+ lists:foldl(
+ fun(Term, {TOs, Ms}) ->
+ case Term of
+ {define_macro, Key, Value} ->
+ case is_atom(Key) and is_all_uppercase(Key) of
+ true ->
+ {TOs, Ms++[{Key, Value}]};
+ false ->
+ exit({macro_not_properly_defined, Term})
+ end;
+ Term ->
+ {TOs ++ [Term], Ms}
+ end
+ end,
+ {[], []},
+ Terms).
+
+%% @doc Recursively replace in Terms macro usages with the defined value.
+%% @spec (Terms, Macros) -> Terms
+%% Terms = [term()]
+%% Macros = [macro()]
+replace([], _) ->
+ [];
+replace([Term|Terms], Macros) ->
+ [replace_term(Term, Macros) | replace(Terms, Macros)].
+
+replace_term(Key, Macros) when is_atom(Key) ->
+ case is_all_uppercase(Key) of
+ true ->
+ case proplists:get_value(Key, Macros) of
+ undefined -> exit({undefined_macro, Key});
+ Value -> Value
+ end;
+ false ->
+ Key
+ end;
+replace_term({use_macro, Key, Value}, Macros) ->
+ proplists:get_value(Key, Macros, Value);
+replace_term(Term, Macros) when is_list(Term) ->
+ replace(Term, Macros);
+replace_term(Term, Macros) when is_tuple(Term) ->
+ List = tuple_to_list(Term),
+ List2 = replace(List, Macros),
+ list_to_tuple(List2);
+replace_term(Term, _) ->
+ Term.
+
+is_all_uppercase(Atom) ->
+ String = erlang:atom_to_list(Atom),
+ (String == string:to_upper(String)).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Process terms
+
process_term(Term, State) ->
case Term of
override_global ->