2019-06-14 11:33:26 +02:00
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
%%% Purpose: Transform old-style Erlang config to YAML config
|
|
|
|
%%%
|
2020-01-28 13:34:02 +01:00
|
|
|
%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
|
2019-06-14 11:33:26 +02:00
|
|
|
%%%
|
|
|
|
%%% This program is free software; you can redistribute it and/or
|
|
|
|
%%% modify it under the terms of the GNU General Public License as
|
|
|
|
%%% published by the Free Software Foundation; either version 2 of the
|
|
|
|
%%% License, or (at your option) any later version.
|
|
|
|
%%%
|
|
|
|
%%% This program is distributed in the hope that it will be useful,
|
|
|
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
%%% General Public License for more details.
|
|
|
|
%%%
|
|
|
|
%%% You should have received a copy of the GNU General Public License along
|
|
|
|
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
%%%
|
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
-module(ejabberd_old_config).
|
|
|
|
|
|
|
|
%% API
|
|
|
|
-export([read_file/1]).
|
|
|
|
|
|
|
|
-include("logger.hrl").
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% API
|
|
|
|
%%%===================================================================
|
|
|
|
read_file(File) ->
|
|
|
|
case consult(File) of
|
|
|
|
{ok, Terms1} ->
|
|
|
|
?INFO_MSG("Converting from old configuration format", []),
|
|
|
|
Terms2 = strings_to_binary(Terms1),
|
|
|
|
Terms3 = transform(Terms2),
|
|
|
|
Terms4 = transform_certfiles(Terms3),
|
|
|
|
Terms5 = transform_host_config(Terms4),
|
|
|
|
{ok, collect_options(Terms5)};
|
|
|
|
{error, Reason} ->
|
|
|
|
{error, {old_config, File, Reason}}
|
|
|
|
end.
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% Internal functions
|
|
|
|
%%%===================================================================
|
|
|
|
collect_options(Opts) ->
|
|
|
|
{D, InvalidOpts} =
|
|
|
|
lists:foldl(
|
|
|
|
fun({K, V}, {D, Os}) when is_list(V) ->
|
|
|
|
{orddict:append_list(K, V, D), Os};
|
|
|
|
({K, V}, {D, Os}) ->
|
|
|
|
{orddict:store(K, V, D), Os};
|
|
|
|
(Opt, {D, Os}) ->
|
|
|
|
{D, [Opt|Os]}
|
|
|
|
end, {orddict:new(), []}, Opts),
|
|
|
|
InvalidOpts ++ orddict:to_list(D).
|
|
|
|
|
|
|
|
transform(Opts) ->
|
|
|
|
Opts1 = transform_register(Opts),
|
|
|
|
Opts2 = transform_s2s(Opts1),
|
|
|
|
Opts3 = transform_listeners(Opts2),
|
2019-07-18 18:31:12 +02:00
|
|
|
Opts5 = transform_sql(Opts3),
|
2019-06-14 11:33:26 +02:00
|
|
|
Opts6 = transform_shaper(Opts5),
|
|
|
|
Opts7 = transform_s2s_out(Opts6),
|
|
|
|
Opts8 = transform_acl(Opts7),
|
|
|
|
Opts9 = transform_modules(Opts8),
|
|
|
|
Opts10 = transform_globals(Opts9),
|
|
|
|
collect_options(Opts10).
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% mod_register
|
|
|
|
%%%===================================================================
|
|
|
|
transform_register(Opts) ->
|
|
|
|
try
|
|
|
|
{value, {modules, ModOpts}, Opts1} = lists:keytake(modules, 1, Opts),
|
|
|
|
{value, {?MODULE, RegOpts}, ModOpts1} = lists:keytake(?MODULE, 1, ModOpts),
|
|
|
|
{value, {ip_access, L}, RegOpts1} = lists:keytake(ip_access, 1, RegOpts),
|
|
|
|
true = is_list(L),
|
|
|
|
?WARNING_MSG("Old 'ip_access' format detected. "
|
|
|
|
"The old format is still supported "
|
|
|
|
"but it is better to fix your config: "
|
|
|
|
"use access rules instead.", []),
|
|
|
|
ACLs = lists:flatmap(
|
|
|
|
fun({Action, S}) ->
|
|
|
|
ACLName = misc:binary_to_atom(
|
|
|
|
iolist_to_binary(
|
|
|
|
["ip_", S])),
|
|
|
|
[{Action, ACLName},
|
|
|
|
{acl, ACLName, {ip, S}}]
|
|
|
|
end, L),
|
|
|
|
Access = {access, mod_register_networks,
|
|
|
|
[{Action, ACLName} || {Action, ACLName} <- ACLs]},
|
|
|
|
[ACL || {acl, _, _} = ACL <- ACLs] ++
|
|
|
|
[Access,
|
|
|
|
{modules,
|
|
|
|
[{mod_register,
|
|
|
|
[{ip_access, mod_register_networks}|RegOpts1]}
|
|
|
|
| ModOpts1]}|Opts1]
|
|
|
|
catch error:{badmatch, false} ->
|
|
|
|
Opts
|
|
|
|
end.
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% ejabberd_s2s
|
|
|
|
%%%===================================================================
|
|
|
|
transform_s2s(Opts) ->
|
|
|
|
lists:foldl(fun transform_s2s/2, [], Opts).
|
|
|
|
|
|
|
|
transform_s2s({{s2s_host, Host}, Action}, Opts) ->
|
|
|
|
?WARNING_MSG("Option 's2s_host' is deprecated.", []),
|
|
|
|
ACLName = misc:binary_to_atom(
|
|
|
|
iolist_to_binary(["s2s_access_", Host])),
|
|
|
|
[{acl, ACLName, {server, Host}},
|
|
|
|
{access, s2s, [{Action, ACLName}]},
|
|
|
|
{s2s_access, s2s} |
|
|
|
|
Opts];
|
|
|
|
transform_s2s({s2s_default_policy, Action}, Opts) ->
|
|
|
|
?WARNING_MSG("Option 's2s_default_policy' is deprecated. "
|
|
|
|
"The option is still supported but it is better to "
|
|
|
|
"fix your config: "
|
|
|
|
"use 's2s_access' with an access rule.", []),
|
|
|
|
[{access, s2s, [{Action, all}]},
|
|
|
|
{s2s_access, s2s} |
|
|
|
|
Opts];
|
|
|
|
transform_s2s(Opt, Opts) ->
|
|
|
|
[Opt|Opts].
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% ejabberd_s2s_out
|
|
|
|
%%%===================================================================
|
|
|
|
transform_s2s_out(Opts) ->
|
|
|
|
lists:foldl(fun transform_s2s_out/2, [], Opts).
|
|
|
|
|
|
|
|
transform_s2s_out({outgoing_s2s_options, Families, Timeout}, Opts) ->
|
|
|
|
?WARNING_MSG("Option 'outgoing_s2s_options' is deprecated. "
|
|
|
|
"The option is still supported "
|
|
|
|
"but it is better to fix your config: "
|
|
|
|
"use 'outgoing_s2s_timeout' and "
|
|
|
|
"'outgoing_s2s_families' instead.", []),
|
|
|
|
[{outgoing_s2s_families, Families},
|
|
|
|
{outgoing_s2s_timeout, Timeout}
|
|
|
|
| Opts];
|
|
|
|
transform_s2s_out({s2s_dns_options, S2SDNSOpts}, AllOpts) ->
|
|
|
|
?WARNING_MSG("Option 's2s_dns_options' is deprecated. "
|
|
|
|
"The option is still supported "
|
|
|
|
"but it is better to fix your config: "
|
|
|
|
"use 's2s_dns_timeout' and "
|
|
|
|
"'s2s_dns_retries' instead", []),
|
|
|
|
lists:foldr(
|
|
|
|
fun({timeout, T}, AccOpts) ->
|
|
|
|
[{s2s_dns_timeout, T}|AccOpts];
|
|
|
|
({retries, R}, AccOpts) ->
|
|
|
|
[{s2s_dns_retries, R}|AccOpts];
|
|
|
|
(_, AccOpts) ->
|
|
|
|
AccOpts
|
|
|
|
end, AllOpts, S2SDNSOpts);
|
|
|
|
transform_s2s_out(Opt, Opts) ->
|
|
|
|
[Opt|Opts].
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% ejabberd_listener
|
|
|
|
%%%===================================================================
|
|
|
|
transform_listeners(Opts) ->
|
|
|
|
lists:foldl(fun transform_listeners/2, [], Opts).
|
|
|
|
|
|
|
|
transform_listeners({listen, LOpts}, Opts) ->
|
|
|
|
[{listen, lists:map(fun transform_listener/1, LOpts)} | Opts];
|
|
|
|
transform_listeners(Opt, Opts) ->
|
|
|
|
[Opt|Opts].
|
|
|
|
|
|
|
|
transform_listener({{Port, IP, Transport}, Mod, Opts}) ->
|
|
|
|
IPStr = if is_tuple(IP) ->
|
|
|
|
list_to_binary(inet_parse:ntoa(IP));
|
|
|
|
true ->
|
|
|
|
IP
|
|
|
|
end,
|
|
|
|
Opts1 = lists:map(
|
|
|
|
fun({ip, IPT}) when is_tuple(IPT) ->
|
|
|
|
{ip, list_to_binary(inet_parse:ntoa(IP))};
|
|
|
|
(ssl) -> {tls, true};
|
|
|
|
(A) when is_atom(A) -> {A, true};
|
|
|
|
(Opt) -> Opt
|
|
|
|
end, Opts),
|
|
|
|
Opts2 = lists:foldl(
|
|
|
|
fun(Opt, Acc) ->
|
|
|
|
transform_listen_option(Mod, Opt, Acc)
|
|
|
|
end, [], Opts1),
|
|
|
|
TransportOpt = if Transport == tcp -> [];
|
|
|
|
true -> [{transport, Transport}]
|
|
|
|
end,
|
|
|
|
IPOpt = if IPStr == <<"0.0.0.0">> -> [];
|
|
|
|
true -> [{ip, IPStr}]
|
|
|
|
end,
|
|
|
|
IPOpt ++ TransportOpt ++ [{port, Port}, {module, Mod} | Opts2];
|
|
|
|
transform_listener({{Port, Transport}, Mod, Opts})
|
|
|
|
when Transport == tcp orelse Transport == udp ->
|
|
|
|
transform_listener({{Port, all_zero_ip(Opts), Transport}, Mod, Opts});
|
|
|
|
transform_listener({{Port, IP}, Mod, Opts}) ->
|
|
|
|
transform_listener({{Port, IP, tcp}, Mod, Opts});
|
|
|
|
transform_listener({Port, Mod, Opts}) ->
|
|
|
|
transform_listener({{Port, all_zero_ip(Opts), tcp}, Mod, Opts});
|
|
|
|
transform_listener(Opt) ->
|
|
|
|
Opt.
|
|
|
|
|
|
|
|
transform_listen_option(ejabberd_http, captcha, Opts) ->
|
|
|
|
[{captcha, true}|Opts];
|
|
|
|
transform_listen_option(ejabberd_http, register, Opts) ->
|
|
|
|
[{register, true}|Opts];
|
|
|
|
transform_listen_option(ejabberd_http, web_admin, Opts) ->
|
|
|
|
[{web_admin, true}|Opts];
|
|
|
|
transform_listen_option(ejabberd_http, http_bind, Opts) ->
|
|
|
|
[{http_bind, true}|Opts];
|
|
|
|
transform_listen_option(ejabberd_http, http_poll, Opts) ->
|
|
|
|
[{http_poll, true}|Opts];
|
|
|
|
transform_listen_option(ejabberd_http, {request_handlers, Hs}, Opts) ->
|
|
|
|
Hs1 = lists:map(
|
|
|
|
fun({PList, Mod}) when is_list(PList) ->
|
|
|
|
Path = iolist_to_binary([[$/, P] || P <- PList]),
|
|
|
|
{Path, Mod};
|
|
|
|
(Opt) ->
|
|
|
|
Opt
|
|
|
|
end, Hs),
|
|
|
|
[{request_handlers, Hs1} | Opts];
|
|
|
|
transform_listen_option(ejabberd_service, {hosts, Hosts, O}, Opts) ->
|
|
|
|
case lists:keyfind(hosts, 1, Opts) of
|
|
|
|
{_, PrevHostOpts} ->
|
|
|
|
NewHostOpts =
|
|
|
|
lists:foldl(
|
|
|
|
fun(H, Acc) ->
|
|
|
|
dict:append_list(H, O, Acc)
|
|
|
|
end, dict:from_list(PrevHostOpts), Hosts),
|
|
|
|
[{hosts, dict:to_list(NewHostOpts)}|
|
|
|
|
lists:keydelete(hosts, 1, Opts)];
|
|
|
|
_ ->
|
|
|
|
[{hosts, [{H, O} || H <- Hosts]}|Opts]
|
|
|
|
end;
|
|
|
|
transform_listen_option(ejabberd_service, {host, Host, Os}, Opts) ->
|
|
|
|
transform_listen_option(ejabberd_service, {hosts, [Host], Os}, Opts);
|
|
|
|
transform_listen_option(ejabberd_xmlrpc, {access_commands, ACOpts}, Opts) ->
|
|
|
|
NewACOpts = lists:map(
|
|
|
|
fun({AName, ACmds, AOpts}) ->
|
|
|
|
{AName, [{commands, ACmds}, {options, AOpts}]};
|
|
|
|
(Opt) ->
|
|
|
|
Opt
|
|
|
|
end, ACOpts),
|
|
|
|
[{access_commands, NewACOpts}|Opts];
|
|
|
|
transform_listen_option(_, Opt, Opts) ->
|
|
|
|
[Opt|Opts].
|
|
|
|
|
|
|
|
-spec all_zero_ip([proplists:property()]) -> inet:ip_address().
|
|
|
|
all_zero_ip(Opts) ->
|
|
|
|
case proplists:get_bool(inet6, Opts) of
|
|
|
|
true -> {0,0,0,0,0,0,0,0};
|
|
|
|
false -> {0,0,0,0}
|
|
|
|
end.
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% ejabberd_shaper
|
|
|
|
%%%===================================================================
|
|
|
|
transform_shaper(Opts) ->
|
|
|
|
lists:foldl(fun transform_shaper/2, [], Opts).
|
|
|
|
|
|
|
|
transform_shaper({shaper, Name, {maxrate, N}}, Opts) ->
|
|
|
|
[{shaper, [{Name, N}]} | Opts];
|
|
|
|
transform_shaper({shaper, Name, none}, Opts) ->
|
|
|
|
[{shaper, [{Name, none}]} | Opts];
|
|
|
|
transform_shaper({shaper, List}, Opts) when is_list(List) ->
|
|
|
|
R = lists:map(
|
|
|
|
fun({Name, Args}) when is_list(Args) ->
|
|
|
|
MaxRate = proplists:get_value(rate, Args, 1000),
|
|
|
|
BurstSize = proplists:get_value(burst_size, Args, MaxRate),
|
|
|
|
{Name, MaxRate, BurstSize};
|
|
|
|
({Name, Val}) ->
|
|
|
|
{Name, Val, Val}
|
|
|
|
end, List),
|
|
|
|
[{shaper, R} | Opts];
|
|
|
|
transform_shaper(Opt, Opts) ->
|
|
|
|
[Opt | Opts].
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% acl
|
|
|
|
%%%===================================================================
|
|
|
|
transform_acl(Opts) ->
|
|
|
|
Opts1 = lists:foldl(fun transform_acl/2, [], Opts),
|
|
|
|
{ACLOpts, Opts2} = lists:mapfoldl(
|
|
|
|
fun({acl, Os}, Acc) ->
|
|
|
|
{Os, Acc};
|
|
|
|
(O, Acc) ->
|
|
|
|
{[], [O|Acc]}
|
|
|
|
end, [], Opts1),
|
|
|
|
{AccessOpts, Opts3} = lists:mapfoldl(
|
|
|
|
fun({access, Os}, Acc) ->
|
|
|
|
{Os, Acc};
|
|
|
|
(O, Acc) ->
|
|
|
|
{[], [O|Acc]}
|
|
|
|
end, [], Opts2),
|
|
|
|
{NewAccessOpts, Opts4} = lists:mapfoldl(
|
|
|
|
fun({access_rules, Os}, Acc) ->
|
|
|
|
{Os, Acc};
|
|
|
|
(O, Acc) ->
|
|
|
|
{[], [O|Acc]}
|
|
|
|
end, [], Opts3),
|
|
|
|
{ShaperOpts, Opts5} = lists:mapfoldl(
|
|
|
|
fun({shaper_rules, Os}, Acc) ->
|
|
|
|
{Os, Acc};
|
|
|
|
(O, Acc) ->
|
|
|
|
{[], [O|Acc]}
|
|
|
|
end, [], Opts4),
|
|
|
|
ACLOpts1 = collect_options(lists:flatten(ACLOpts)),
|
|
|
|
AccessOpts1 = case collect_options(lists:flatten(AccessOpts)) of
|
|
|
|
[] -> [];
|
|
|
|
L1 -> [{access, L1}]
|
|
|
|
end,
|
|
|
|
ACLOpts2 = case lists:map(
|
|
|
|
fun({ACLName, Os}) ->
|
|
|
|
{ACLName, collect_options(Os)}
|
|
|
|
end, ACLOpts1) of
|
|
|
|
[] -> [];
|
|
|
|
L2 -> [{acl, L2}]
|
|
|
|
end,
|
|
|
|
NewAccessOpts1 = case lists:map(
|
|
|
|
fun({NAName, Os}) ->
|
|
|
|
{NAName, transform_access_rules_config(Os)}
|
|
|
|
end, lists:flatten(NewAccessOpts)) of
|
|
|
|
[] -> [];
|
|
|
|
L3 -> [{access_rules, L3}]
|
|
|
|
end,
|
|
|
|
ShaperOpts1 = case lists:map(
|
|
|
|
fun({SName, Ss}) ->
|
|
|
|
{SName, transform_access_rules_config(Ss)}
|
|
|
|
end, lists:flatten(ShaperOpts)) of
|
|
|
|
[] -> [];
|
|
|
|
L4 -> [{shaper_rules, L4}]
|
|
|
|
end,
|
|
|
|
ACLOpts2 ++ AccessOpts1 ++ NewAccessOpts1 ++ ShaperOpts1 ++ Opts5.
|
|
|
|
|
|
|
|
transform_acl({acl, Name, Type}, Opts) ->
|
|
|
|
T = case Type of
|
|
|
|
all -> all;
|
|
|
|
none -> none;
|
|
|
|
{user, U} -> {user, [b(U)]};
|
|
|
|
{user, U, S} -> {user, [[{b(U), b(S)}]]};
|
|
|
|
{shared_group, G} -> {shared_group, [b(G)]};
|
|
|
|
{shared_group, G, H} -> {shared_group, [[{b(G), b(H)}]]};
|
|
|
|
{user_regexp, UR} -> {user_regexp, [b(UR)]};
|
|
|
|
{user_regexp, UR, S} -> {user_regexp, [[{b(UR), b(S)}]]};
|
|
|
|
{node_regexp, UR, SR} -> {node_regexp, [[{b(UR), b(SR)}]]};
|
|
|
|
{user_glob, UR} -> {user_glob, [b(UR)]};
|
|
|
|
{user_glob, UR, S} -> {user_glob, [[{b(UR), b(S)}]]};
|
|
|
|
{node_glob, UR, SR} -> {node_glob, [[{b(UR), b(SR)}]]};
|
|
|
|
{server, S} -> {server, [b(S)]};
|
|
|
|
{resource, R} -> {resource, [b(R)]};
|
|
|
|
{server_regexp, SR} -> {server_regexp, [b(SR)]};
|
|
|
|
{server_glob, S} -> {server_glob, [b(S)]};
|
|
|
|
{ip, S} -> {ip, [b(S)]};
|
|
|
|
{resource_glob, R} -> {resource_glob, [b(R)]};
|
|
|
|
{resource_regexp, R} -> {resource_regexp, [b(R)]}
|
|
|
|
end,
|
|
|
|
[{acl, [{Name, [T]}]}|Opts];
|
|
|
|
transform_acl({access, Name, Rules}, Opts) ->
|
|
|
|
NewRules = [{ACL, Action} || {Action, ACL} <- Rules],
|
|
|
|
[{access, [{Name, NewRules}]}|Opts];
|
|
|
|
transform_acl(Opt, Opts) ->
|
|
|
|
[Opt|Opts].
|
|
|
|
|
|
|
|
transform_access_rules_config(Config) when is_list(Config) ->
|
|
|
|
lists:map(fun transform_access_rules_config2/1, lists:flatten(Config));
|
|
|
|
transform_access_rules_config(Config) ->
|
|
|
|
transform_access_rules_config([Config]).
|
|
|
|
|
|
|
|
transform_access_rules_config2(Type) when is_integer(Type); is_atom(Type) ->
|
|
|
|
{Type, [all]};
|
|
|
|
transform_access_rules_config2({Type, ACL}) when is_atom(ACL) ->
|
|
|
|
{Type, [{acl, ACL}]};
|
|
|
|
transform_access_rules_config2({Res, Rules}) when is_list(Rules) ->
|
|
|
|
T = lists:map(fun({Type, Args}) when is_list(Args) ->
|
|
|
|
{Type, hd(lists:flatten(Args))};
|
|
|
|
(V) ->
|
|
|
|
V
|
|
|
|
end, lists:flatten(Rules)),
|
|
|
|
{Res, T};
|
|
|
|
transform_access_rules_config2({Res, Rule}) ->
|
|
|
|
{Res, [Rule]}.
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% SQL
|
|
|
|
%%%===================================================================
|
|
|
|
-define(PGSQL_PORT, 5432).
|
|
|
|
-define(MYSQL_PORT, 3306).
|
|
|
|
|
|
|
|
transform_sql(Opts) ->
|
|
|
|
lists:foldl(fun transform_sql/2, [], Opts).
|
|
|
|
|
|
|
|
transform_sql({odbc_server, {Type, Server, Port, DB, User, Pass}}, Opts) ->
|
|
|
|
[{sql_type, Type},
|
|
|
|
{sql_server, Server},
|
|
|
|
{sql_port, Port},
|
|
|
|
{sql_database, DB},
|
|
|
|
{sql_username, User},
|
|
|
|
{sql_password, Pass}|Opts];
|
|
|
|
transform_sql({odbc_server, {mysql, Server, DB, User, Pass}}, Opts) ->
|
|
|
|
transform_sql({odbc_server, {mysql, Server, ?MYSQL_PORT, DB, User, Pass}}, Opts);
|
|
|
|
transform_sql({odbc_server, {pgsql, Server, DB, User, Pass}}, Opts) ->
|
|
|
|
transform_sql({odbc_server, {pgsql, Server, ?PGSQL_PORT, DB, User, Pass}}, Opts);
|
|
|
|
transform_sql({odbc_server, {sqlite, DB}}, Opts) ->
|
|
|
|
[{sql_type, sqlite},
|
|
|
|
{sql_database, DB}|Opts];
|
|
|
|
transform_sql({odbc_pool_size, N}, Opts) ->
|
|
|
|
[{sql_pool_size, N}|Opts];
|
|
|
|
transform_sql(Opt, Opts) ->
|
|
|
|
[Opt|Opts].
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% modules
|
|
|
|
%%%===================================================================
|
|
|
|
transform_modules(Opts) ->
|
|
|
|
lists:foldl(fun transform_modules/2, [], Opts).
|
|
|
|
|
|
|
|
transform_modules({modules, ModOpts}, Opts) ->
|
|
|
|
[{modules, lists:map(
|
|
|
|
fun({Mod, Opts1}) ->
|
|
|
|
{Mod, transform_module(Mod, Opts1)};
|
|
|
|
(Other) ->
|
|
|
|
Other
|
|
|
|
end, ModOpts)}|Opts];
|
|
|
|
transform_modules(Opt, Opts) ->
|
|
|
|
[Opt|Opts].
|
|
|
|
|
|
|
|
transform_module(mod_disco, Opts) ->
|
|
|
|
lists:map(
|
|
|
|
fun({server_info, Infos}) ->
|
|
|
|
NewInfos = lists:map(
|
|
|
|
fun({Modules, Name, URLs}) ->
|
|
|
|
[[{modules, Modules},
|
|
|
|
{name, Name},
|
|
|
|
{urls, URLs}]];
|
|
|
|
(Opt) ->
|
|
|
|
Opt
|
|
|
|
end, Infos),
|
|
|
|
{server_info, NewInfos};
|
|
|
|
(Opt) ->
|
|
|
|
Opt
|
|
|
|
end, Opts);
|
|
|
|
transform_module(mod_muc_log, Opts) ->
|
|
|
|
lists:map(
|
|
|
|
fun({top_link, {S1, S2}}) ->
|
|
|
|
{top_link, [{S1, S2}]};
|
|
|
|
(Opt) ->
|
|
|
|
Opt
|
|
|
|
end, Opts);
|
|
|
|
transform_module(mod_proxy65, Opts) ->
|
|
|
|
lists:map(
|
|
|
|
fun({ip, IP}) when is_tuple(IP) ->
|
|
|
|
{ip, misc:ip_to_list(IP)};
|
|
|
|
({hostname, IP}) when is_tuple(IP) ->
|
|
|
|
{hostname, misc:ip_to_list(IP)};
|
|
|
|
(Opt) ->
|
|
|
|
Opt
|
|
|
|
end, Opts);
|
|
|
|
transform_module(mod_register, Opts) ->
|
|
|
|
lists:flatmap(
|
|
|
|
fun({welcome_message, {Subj, Body}}) ->
|
|
|
|
[{welcome_message, [{subject, Subj}, {body, Body}]}];
|
|
|
|
(Opt) ->
|
|
|
|
[Opt]
|
|
|
|
end, Opts);
|
|
|
|
transform_module(_Mod, Opts) ->
|
|
|
|
Opts.
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% Host config
|
|
|
|
%%%===================================================================
|
|
|
|
transform_host_config(Opts) ->
|
|
|
|
Opts1 = lists:foldl(fun transform_host_config/2, [], Opts),
|
|
|
|
{HOpts, Opts2} = lists:mapfoldl(
|
|
|
|
fun({host_config, O}, Os) ->
|
|
|
|
{[O], Os};
|
|
|
|
(O, Os) ->
|
|
|
|
{[], [O|Os]}
|
|
|
|
end, [], Opts1),
|
|
|
|
{AHOpts, Opts3} = lists:mapfoldl(
|
|
|
|
fun({append_host_config, O}, Os) ->
|
|
|
|
{[O], Os};
|
|
|
|
(O, Os) ->
|
|
|
|
{[], [O|Os]}
|
|
|
|
end, [], Opts2),
|
|
|
|
HOpts1 = case collect_options(lists:flatten(HOpts)) of
|
|
|
|
[] ->
|
|
|
|
[];
|
|
|
|
HOs ->
|
|
|
|
[{host_config,
|
|
|
|
[{H, transform(O)} || {H, O} <- HOs]}]
|
|
|
|
end,
|
|
|
|
AHOpts1 = case collect_options(lists:flatten(AHOpts)) of
|
|
|
|
[] ->
|
|
|
|
[];
|
|
|
|
AHOs ->
|
|
|
|
[{append_host_config,
|
|
|
|
[{H, transform(O)} || {H, O} <- AHOs]}]
|
|
|
|
end,
|
|
|
|
HOpts1 ++ AHOpts1 ++ Opts3.
|
|
|
|
|
|
|
|
transform_host_config({host_config, Host, HOpts}, Opts) ->
|
|
|
|
{AddOpts, HOpts1} =
|
|
|
|
lists:mapfoldl(
|
|
|
|
fun({{add, Opt}, Val}, Os) ->
|
|
|
|
{[{Opt, Val}], Os};
|
|
|
|
(O, Os) ->
|
|
|
|
{[], [O|Os]}
|
|
|
|
end, [], HOpts),
|
|
|
|
[{append_host_config, [{Host, lists:flatten(AddOpts)}]},
|
|
|
|
{host_config, [{Host, HOpts1}]}|Opts];
|
|
|
|
transform_host_config(Opt, Opts) ->
|
|
|
|
[Opt|Opts].
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% Top-level options
|
|
|
|
%%%===================================================================
|
|
|
|
transform_globals(Opts) ->
|
|
|
|
lists:foldl(fun transform_globals/2, [], Opts).
|
|
|
|
|
|
|
|
transform_globals(Opt, Opts) when Opt == override_global;
|
|
|
|
Opt == override_local;
|
|
|
|
Opt == override_acls ->
|
2019-09-23 14:17:20 +02:00
|
|
|
?WARNING_MSG("Option '~ts' has no effect anymore", [Opt]),
|
2019-06-14 11:33:26 +02:00
|
|
|
Opts;
|
|
|
|
transform_globals({node_start, _}, Opts) ->
|
|
|
|
?WARNING_MSG("Option 'node_start' has no effect anymore", []),
|
|
|
|
Opts;
|
|
|
|
transform_globals({iqdisc, {queues, N}}, Opts) ->
|
|
|
|
[{iqdisc, N}|Opts];
|
|
|
|
transform_globals({define_macro, Macro, Val}, Opts) ->
|
|
|
|
[{define_macro, [{Macro, Val}]}|Opts];
|
|
|
|
transform_globals(Opt, Opts) ->
|
|
|
|
[Opt|Opts].
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% Certfiles
|
|
|
|
%%%===================================================================
|
|
|
|
transform_certfiles(Opts) ->
|
|
|
|
lists:foldl(fun transform_certfiles/2, [], Opts).
|
|
|
|
|
|
|
|
transform_certfiles({domain_certfile, Domain, CertFile}, Opts) ->
|
|
|
|
[{host_config, [{Domain, [{domain_certfile, CertFile}]}]}|Opts];
|
|
|
|
transform_certfiles(Opt, Opts) ->
|
|
|
|
[Opt|Opts].
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% Consult file
|
|
|
|
%%%===================================================================
|
|
|
|
consult(File) ->
|
|
|
|
case file:consult(File) of
|
|
|
|
{ok, Terms} ->
|
|
|
|
include_config_files(Terms);
|
|
|
|
Err ->
|
|
|
|
Err
|
|
|
|
end.
|
|
|
|
|
|
|
|
include_config_files(Terms) ->
|
|
|
|
include_config_files(Terms, []).
|
|
|
|
|
|
|
|
include_config_files([], Res) ->
|
|
|
|
{ok, 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) ->
|
|
|
|
case consult(Filename) of
|
|
|
|
{ok, Included_terms} ->
|
|
|
|
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);
|
|
|
|
Err ->
|
|
|
|
Err
|
|
|
|
end;
|
|
|
|
include_config_files([Term | Terms], Res) ->
|
|
|
|
include_config_files(Terms, Res ++ [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 ->
|
|
|
|
delete_disallowed2(Disallowed, T);
|
|
|
|
_ ->
|
|
|
|
[H|delete_disallowed2(Disallowed, T)]
|
|
|
|
end;
|
|
|
|
delete_disallowed2(_, []) ->
|
|
|
|
[].
|
|
|
|
|
|
|
|
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),
|
|
|
|
As.
|
|
|
|
|
|
|
|
%%%===================================================================
|
|
|
|
%%% Aux functions
|
|
|
|
%%%===================================================================
|
|
|
|
strings_to_binary([]) ->
|
|
|
|
[];
|
|
|
|
strings_to_binary(L) when is_list(L) ->
|
|
|
|
case is_string(L) of
|
|
|
|
true ->
|
|
|
|
list_to_binary(L);
|
|
|
|
false ->
|
|
|
|
strings_to_binary1(L)
|
|
|
|
end;
|
|
|
|
strings_to_binary({A, B, C, D}) when
|
|
|
|
is_integer(A), is_integer(B), is_integer(C), is_integer(D) ->
|
|
|
|
{A, B, C ,D};
|
|
|
|
strings_to_binary(T) when is_tuple(T) ->
|
|
|
|
list_to_tuple(strings_to_binary1(tuple_to_list(T)));
|
|
|
|
strings_to_binary(X) ->
|
|
|
|
X.
|
|
|
|
|
|
|
|
strings_to_binary1([El|L]) ->
|
|
|
|
[strings_to_binary(El)|strings_to_binary1(L)];
|
|
|
|
strings_to_binary1([]) ->
|
|
|
|
[];
|
|
|
|
strings_to_binary1(T) ->
|
|
|
|
T.
|
|
|
|
|
|
|
|
is_string([C|T]) when (C >= 0) and (C =< 255) ->
|
|
|
|
is_string(T);
|
|
|
|
is_string([]) ->
|
|
|
|
true;
|
|
|
|
is_string(_) ->
|
|
|
|
false.
|
|
|
|
|
|
|
|
b(S) ->
|
|
|
|
iolist_to_binary(S).
|