diff options
author | Evgeniy Khramtsov <ekhramtsov@process-one.net> | 2013-08-12 16:25:05 +0400 |
---|---|---|
committer | Evgeniy Khramtsov <ekhramtsov@process-one.net> | 2013-08-21 16:17:59 +0400 |
commit | 91a74e3e27cf2c2f6b25888c3cc276d0ddbf5c69 (patch) | |
tree | a3838e249275ca587018d865fade3f3c41e9eddb /src | |
parent | f68dfacbbf3b7489571de61f61818576fb836a63 (diff) |
Change configuration file format to YAML
Diffstat (limited to 'src')
48 files changed, 1299 insertions, 674 deletions
diff --git a/src/acl.erl b/src/acl.erl index 1338e55b6..4c4523617 100644 --- a/src/acl.erl +++ b/src/acl.erl @@ -29,35 +29,38 @@ -author('alexey@process-one.net'). -export([start/0, to_record/3, add/3, add_list/3, - add_local/3, add_list_local/3, - match_rule/3, match_acl/3]). + add_local/3, add_list_local/3, load_from_config/0, + match_rule/3, match_acl/3, transform_options/1]). -include("ejabberd.hrl"). -include("logger.hrl"). -include("jlib.hrl"). -record(acl, {aclname, aclspec}). +-record(access, {name :: access_name(), + rules = [] :: [access_rule()]}). -type regexp() :: binary(). -type glob() :: binary(). +-type access_name() :: atom(). +-type access_rule() :: {atom(), any()}. +-type host() :: binary(). -type aclname() :: {atom(), binary() | global}. -type aclspec() :: all | none | - {user, binary()} | - {user, binary(), binary()} | + {user, {binary(), host()} | binary()} | {server, binary()} | {resource, binary()} | - {user_regexp, regexp()} | - {shared_group, binary()} | - {shared_group, binary(), binary()} | - {user_regexp, regexp(), binary()} | + {user_regexp, {regexp(), host()} | regexp()} | + {shared_group, {binary(), host()} | binary()} | + {user_regexp, {regexp(), host()} | regexp()} | {server_regexp, regexp()} | {resource_regexp, regexp()} | - {node_regexp, regexp(), regexp()} | - {user_glob, glob()} | - {user_glob, glob(), binary()} | + {node_regexp, {regexp(), regexp()}} | + {user_glob, {glob(), host()} | glob()} | {server_glob, glob()} | {resource_glob, glob()} | - {node_glob, glob(), glob()}. + {ip, {inet:ip_address(), integer()}} | + {node_glob, {glob(), glob()}}. -type acl() :: #acl{aclname :: aclname(), aclspec :: aclspec()}. @@ -65,12 +68,23 @@ -export_type([acl/0]). start() -> + case catch mnesia:table_info(acl, storage_type) of + disc_copies -> + mnesia:delete_table(acl); + _ -> + ok + end, mnesia:create_table(acl, - [{disc_copies, [node()]}, {type, bag}, + [{ram_copies, [node()]}, {type, bag}, {local_content, true}, {attributes, record_info(fields, acl)}]), + mnesia:create_table(access, + [{ram_copies, [node()]}, + {local_content, true}, + {attributes, record_info(fields, access)}]), mnesia:add_table_copy(acl, node(), ram_copies), - update_table(), + mnesia:add_table_copy(access, node(), ram_copies), + load_from_config(), ok. -spec to_record(binary(), atom(), aclspec()) -> acl(). @@ -82,7 +96,7 @@ to_record(Host, ACLName, ACLSpec) -> -spec add(binary(), aclname(), aclspec()) -> ok | {error, any()}. add(Host, ACLName, ACLSpec) -> - {ResL, BadNodes} = rpc:multicall(ejabberd_cluster:get_nodes(), + {ResL, BadNodes} = rpc:multicall(mnesia:system_info(running_db_nodes), ?MODULE, add_local, [Host, ACLName, ACLSpec]), case lists:keyfind(aborted, 1, ResL) of @@ -109,7 +123,7 @@ add_local(Host, ACLName, ACLSpec) -> -spec add_list(binary(), [acl()], boolean()) -> ok | {error, any()}. add_list(Host, ACLs, Clear) -> - {ResL, BadNodes} = rpc:multicall(ejabberd_cluster:get_nodes(), + {ResL, BadNodes} = rpc:multicall(mnesia:system_info(running_db_nodes), ?MODULE, add_list_local, [Host, ACLs, Clear]), case lists:keyfind(aborted, 1, ResL) of @@ -147,130 +161,196 @@ add_list_local(Host, ACLs, Clear) -> end, mnesia:transaction(F). -normalize(A) -> jlib:nodeprep(iolist_to_binary(A)). - -normalize_spec({A, B}) -> {A, normalize(B)}; -normalize_spec({A, B, C}) -> - {A, normalize(B), normalize(C)}; -normalize_spec(all) -> all; -normalize_spec(none) -> none. - --spec match_rule(global | binary(), atom(), jid() | ljid()) -> any(). - -match_rule(global, Rule, JID) -> - case Rule of - all -> allow; - none -> deny; - _ -> - case ejabberd_config:get_global_option( - {access, Rule, global}, fun(V) -> V end) - of - undefined -> deny; - GACLs -> match_acls(GACLs, JID, global) - end - end; -match_rule(Host, Rule, JID) -> - case Rule of - all -> allow; - none -> deny; - _ -> - case ejabberd_config:get_global_option( - {access, Rule, global}, fun(V) -> V end) - of - undefined -> - case ejabberd_config:get_global_option( - {access, Rule, Host}, fun(V) -> V end) - of - undefined -> deny; - ACLs -> match_acls(ACLs, JID, Host) - end; - GACLs -> - case ejabberd_config:get_global_option( - {access, Rule, Host}, fun(V) -> V end) - of - undefined -> match_acls(GACLs, JID, Host); - ACLs -> - case lists:reverse(GACLs) of - [{allow, all} | Rest] -> - match_acls(lists:reverse(Rest) ++ - ACLs ++ [{allow, all}], - JID, Host); - _ -> match_acls(GACLs ++ ACLs, JID, Host) - end - end - end +-spec add_access(binary() | global, + access_name(), [access_rule()]) -> ok | {error, any()}. + +add_access(Host, Access, Rules) -> + case mnesia:transaction( + fun() -> + mnesia:write( + #access{name = {Access, Host}, + rules = Rules}) + end) of + {atomic, ok} -> + ok; + Err -> + {error, Err} + end. + +-spec load_from_config() -> ok. + +load_from_config() -> + Hosts = [global|?MYHOSTS], + lists:foreach( + fun(Host) -> + ACLs = ejabberd_config:get_option( + {acl, Host}, fun(V) -> V end, []), + AccessRules = ejabberd_config:get_option( + {access, Host}, fun(V) -> V end, []), + lists:foreach( + fun({ACLName, SpecList}) -> + lists:foreach( + fun({ACLType, ACLSpecs}) when is_list(ACLSpecs) -> + lists:foreach( + fun(ACLSpec) -> + add(Host, ACLName, + {ACLType, ACLSpec}) + end, lists:flatten(ACLSpecs)); + ({ACLType, ACLSpecs}) -> + add(Host, ACLName, {ACLType, ACLSpecs}) + end, lists:flatten(SpecList)) + end, ACLs), + lists:foreach( + fun({Access, Rules}) -> + add_access(Host, Access, Rules) + end, AccessRules) + end, Hosts). + +b(S) -> + iolist_to_binary(S). + +nodeprep(S) -> + jlib:nodeprep(b(S)). + +nameprep(S) -> + jlib:nameprep(b(S)). + +resourceprep(S) -> + jlib:resourceprep(b(S)). + +normalize_spec(Spec) -> + case Spec of + all -> all; + none -> none; + {user, {U, S}} -> {user, {nodeprep(U), nameprep(S)}}; + {user, U} -> {user, nodeprep(U)}; + {shared_group, {G, H}} -> {shared_group, {b(G), nameprep(H)}}; + {shared_group, G} -> {shared_group, b(G)}; + {user_regexp, {UR, S}} -> {user_regexp, {b(UR), nameprep(S)}}; + {user_regexp, UR} -> {user_regexp, b(UR)}; + {node_regexp, {UR, SR}} -> {node_regexp, {b(UR), b(SR)}}; + {user_glob, {UR, S}} -> {user_glob, {b(UR), nameprep(S)}}; + {user_glob, UR} -> {user_glob, b(UR)}; + {node_glob, {UR, SR}} -> {node_glob, {b(UR), b(SR)}}; + {server, S} -> {server, nameprep(S)}; + {resource, R} -> {resource, resourceprep(R)}; + {server_regexp, SR} -> {server_regexp, b(SR)}; + {server_glob, S} -> {server_glob, b(S)}; + {resource_glob, R} -> {resource_glob, b(R)}; + {ip, S} -> + case parse_ip_netmask(b(S)) of + {ok, Net, Mask} -> + {ip, {Net, Mask}}; + error -> + ?INFO_MSG("Invalid network address: ~p", [S]), + none + end + end. + +-spec match_rule(global | binary(), access_name(), + jid() | ljid() | inet:ip_address()) -> any(). + +match_rule(_Host, all, _JID) -> + allow; +match_rule(_Host, none, _JID) -> + deny; +match_rule(Host, Access, JID) -> + GAccess = ets:lookup(access, {Access, global}), + LAccess = if Host /= global -> + ets:lookup(access, {Access, Host}); + true -> + [] + end, + case GAccess ++ LAccess of + [] -> + ?WARNING_MSG("Attempt to match against unspecified " + "access rule '~s' (scope: ~s)", + [Access, Host]), + deny; + AccessList -> + Rules = lists:flatmap( + fun(#access{rules = Rs}) -> + Rs + end, AccessList), + match_acls(Rules, JID, Host) end. match_acls([], _, _Host) -> deny; -match_acls([{Access, ACL} | ACLs], JID, Host) -> +match_acls([{ACL, Access} | ACLs], JID, Host) -> case match_acl(ACL, JID, Host) of true -> Access; _ -> match_acls(ACLs, JID, Host) end. --spec match_acl(atom(), jid() | ljid(), binary()) -> boolean(). +-spec match_acl(atom(), + jid() | ljid() | inet:ip_address(), + binary()) -> boolean(). +match_acl(all, _JID, _Host) -> + true; +match_acl(none, _JID, _Host) -> + false; +match_acl(ACL, IP, Host) when tuple_size(IP) == 4; + tuple_size(IP) == 8 -> + lists:any( + fun(#acl{aclspec = {ip, {Net, Mask}}}) -> + is_ip_match(IP, Net, Mask); + (_) -> + false + end, + ets:lookup(acl, {ACL, Host}) ++ + ets:lookup(acl, {ACL, global})); match_acl(ACL, JID, Host) -> - case ACL of - all -> true; - none -> false; - _ -> - {User, Server, Resource} = jlib:jid_tolower(JID), - lists:any(fun (#acl{aclspec = Spec}) -> - case Spec of - all -> true; - {user, U} -> - U == User andalso - (Host == Server orelse - Host == global andalso - lists:member(Server, ?MYHOSTS)); - {user, U, S} -> U == User andalso S == Server; - {server, S} -> S == Server; - {resource, R} -> R == Resource; - {user_regexp, UR} -> - (Host == Server orelse - Host == global andalso - lists:member(Server, ?MYHOSTS)) - andalso is_regexp_match(User, UR); - {shared_group, G} -> - Mod = loaded_shared_roster_module(Host), - Mod:is_user_in_group({User, Server}, G, Host); - {shared_group, G, H} -> - Mod = loaded_shared_roster_module(H), - Mod:is_user_in_group({User, Server}, G, H); - {user_regexp, UR, S} -> - S == Server andalso is_regexp_match(User, UR); - {server_regexp, SR} -> - is_regexp_match(Server, SR); - {resource_regexp, RR} -> - is_regexp_match(Resource, RR); - {node_regexp, UR, SR} -> - is_regexp_match(Server, SR) andalso - is_regexp_match(User, UR); - {user_glob, UR} -> - (Host == Server orelse - Host == global andalso - lists:member(Server, ?MYHOSTS)) - andalso is_glob_match(User, UR); - {user_glob, UR, S} -> - S == Server andalso is_glob_match(User, UR); - {server_glob, SR} -> is_glob_match(Server, SR); - {resource_glob, RR} -> - is_glob_match(Resource, RR); - {node_glob, UR, SR} -> - is_glob_match(Server, SR) andalso - is_glob_match(User, UR); - WrongSpec -> - ?ERROR_MSG("Wrong ACL expression: ~p~nCheck your " - "config file and reload it with the override_a" - "cls option enabled", - [WrongSpec]), - false - end - end, - ets:lookup(acl, {ACL, global}) ++ - ets:lookup(acl, {ACL, Host})) - end. + {User, Server, Resource} = jlib:jid_tolower(JID), + lists:any( + fun(#acl{aclspec = Spec}) -> + case Spec of + all -> true; + {user, {U, S}} -> U == User andalso S == Server; + {user, U} -> + U == User andalso + lists:member(Server, ?MYHOSTS); + {server, S} -> S == Server; + {resource, R} -> R == Resource; + {shared_group, {G, H}} -> + Mod = loaded_shared_roster_module(H), + Mod:is_user_in_group({User, Server}, G, H); + {shared_group, G} -> + Mod = loaded_shared_roster_module(Host), + Mod:is_user_in_group({User, Server}, G, Host); + {user_regexp, {UR, S}} -> + S == Server andalso is_regexp_match(User, UR); + {user_regexp, UR} -> + lists:member(Server, ?MYHOSTS) + andalso is_regexp_match(User, UR); + {server_regexp, SR} -> + is_regexp_match(Server, SR); + {resource_regexp, RR} -> + is_regexp_match(Resource, RR); + {node_regexp, {UR, SR}} -> + is_regexp_match(Server, SR) andalso + is_regexp_match(User, UR); + {user_glob, {UR, S}} -> + S == Server andalso is_glob_match(User, UR); + {user_glob, UR} -> + lists:member(Server, ?MYHOSTS) + andalso is_glob_match(User, UR); + {server_glob, SR} -> is_glob_match(Server, SR); + {resource_glob, RR} -> + is_glob_match(Resource, RR); + {node_glob, {UR, SR}} -> + is_glob_match(Server, SR) andalso + is_glob_match(User, UR); + WrongSpec -> + ?ERROR_MSG("Wrong ACL expression: ~p~nCheck your " + "config file and reload it with the override_a" + "cls option enabled", + [WrongSpec]), + false + end + end, + ets:lookup(acl, {ACL, Host}) ++ + ets:lookup(acl, {ACL, global})). is_regexp_match(String, RegExp) -> case ejabberd_regexp:run(String, RegExp) of @@ -286,34 +366,115 @@ is_glob_match(String, Glob) -> is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)). +is_ip_match({_, _, _, _} = IP, {_, _, _, _} = Net, Mask) -> + IPInt = ip_to_integer(IP), + NetInt = ip_to_integer(Net), + M = bnot (1 bsl (32 - Mask) - 1), + IPInt band M =:= NetInt band M; +is_ip_match({_, _, _, _, _, _, _, _} = IP, + {_, _, _, _, _, _, _, _} = Net, Mask) -> + IPInt = ip_to_integer(IP), + NetInt = ip_to_integer(Net), + M = bnot (1 bsl (128 - Mask) - 1), + IPInt band M =:= NetInt band M; +is_ip_match(_, _, _) -> + false. + +ip_to_integer({IP1, IP2, IP3, IP4}) -> + IP1 bsl 8 bor IP2 bsl 8 bor IP3 bsl 8 bor IP4; +ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7, + IP8}) -> + IP1 bsl 16 bor IP2 bsl 16 bor IP3 bsl 16 bor IP4 bsl 16 + bor IP5 + bsl 16 + bor IP6 + bsl 16 + bor IP7 + bsl 16 + bor IP8. + loaded_shared_roster_module(Host) -> case gen_mod:is_loaded(Host, mod_shared_roster_ldap) of true -> mod_shared_roster_ldap; false -> mod_shared_roster end. -update_table() -> - Fields = record_info(fields, acl), - case mnesia:table_info(acl, attributes) of - Fields -> - ejabberd_config:convert_table_to_binary( - acl, Fields, bag, - fun(#acl{aclspec = Spec}) when is_tuple(Spec) -> - element(2, Spec); - (_) -> - '$next' - end, - fun(#acl{aclname = {ACLName, Host}, - aclspec = Spec} = R) -> - NewHost = if Host == global -> - Host; - true -> - iolist_to_binary(Host) - end, - R#acl{aclname = {ACLName, NewHost}, - aclspec = normalize_spec(Spec)} - end); - _ -> - ?INFO_MSG("Recreating acl table", []), - mnesia:transform_table(acl, ignore, Fields) +parse_ip_netmask(S) -> + case str:tokens(S, <<"/">>) of + [IPStr] -> + case inet_parse:address(binary_to_list(IPStr)) of + {ok, {_, _, _, _} = IP} -> {ok, IP, 32}; + {ok, {_, _, _, _, _, _, _, _} = IP} -> {ok, IP, 128}; + _ -> error + end; + [IPStr, MaskStr] -> + case catch jlib:binary_to_integer(MaskStr) of + Mask when is_integer(Mask), Mask >= 0 -> + case inet_parse:address(binary_to_list(IPStr)) of + {ok, {_, _, _, _} = IP} when Mask =< 32 -> + {ok, IP, Mask}; + {ok, {_, _, _, _, _, _, _, _} = IP} when Mask =< 128 -> + {ok, IP, Mask}; + _ -> error + end; + _ -> error + end; + _ -> error end. + +transform_options(Opts) -> + Opts1 = lists:foldl(fun transform_options/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), + ACLOpts1 = ejabberd_config:collect_options(lists:flatten(ACLOpts)), + AccessOpts1 = case ejabberd_config:collect_options( + lists:flatten(AccessOpts)) of + [] -> []; + L1 -> [{access, L1}] + end, + ACLOpts2 = case lists:map( + fun({ACLName, Os}) -> + {ACLName, ejabberd_config:collect_options(Os)} + end, ACLOpts1) of + [] -> []; + L2 -> [{acl, L2}] + end, + ACLOpts2 ++ AccessOpts1 ++ Opts3. + +transform_options({acl, Name, Type}, Opts) -> + T = case Type of + all -> all; + none -> none; + {user, U} -> {user, [U]}; + {user, U, S} -> {user, [[{U, S}]]}; + {shared_group, G} -> {shared_group, [G]}; + {shared_group, G, H} -> {shared_group, [[{G, H}]]}; + {user_regexp, UR} -> {user_regexp, [UR]}; + {user_regexp, UR, S} -> {user_regexp, [[{UR, S}]]}; + {node_regexp, UR, SR} -> {node_regexp, [[{UR, SR}]]}; + {user_glob, UR} -> {user_glob, [UR]}; + {user_glob, UR, S} -> {user_glob, [[{UR, S}]]}; + {node_glob, UR, SR} -> {node_glob, [[{UR, SR}]]}; + {server, S} -> {server, [S]}; + {resource, R} -> {resource, [R]}; + {server_regexp, SR} -> {server_regexp, [SR]}; + {server_glob, S} -> {server_glob, [S]}; + {ip, S} -> {ip, [S]}; + {resource_glob, R} -> {resource_glob, [R]} + end, + [{acl, [{Name, [T]}]}|Opts]; +transform_options({access, Name, Rules}, Opts) -> + NewRules = [{ACL, Action} || {Action, ACL} <- Rules], + [{access, [{Name, NewRules}]}|Opts]; +transform_options(Opt, Opts) -> + [Opt|Opts]. diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl index ecb50c159..3f7133943 100644 --- a/src/cyrsasl_digest.erl +++ b/src/cyrsasl_digest.erl @@ -204,11 +204,11 @@ get_local_fqdn() -> Str when is_binary(Str) -> Str; _ -> <<"unknown-fqdn, please configure fqdn " - "option in ejabberd.cfg!">> + "option in ejabberd.yml!">> end. get_local_fqdn2() -> - case ejabberd_config:get_local_option( + case ejabberd_config:get_option( fqdn, fun iolist_to_binary/1) of ConfiguredFqdn when is_binary(ConfiguredFqdn) -> ConfiguredFqdn; diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 5218d588a..a21331536 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -164,6 +164,12 @@ commands() -> module = ejd2odbc, function = export, args = [{host, string}, {file, string}], result = {res, rescode}}, + #ejabberd_commands{name = convert_to_yaml, tags = [config], + desc = "Convert the input file from Erlang to YAML format", + module = ejabberd_config, function = convert_to_yaml, + args = [{in, string}, {out, string}], + result = {res, rescode}}, + #ejabberd_commands{name = delete_expired_messages, tags = [purge], desc = "Delete expired offline messages from database", module = ?MODULE, function = delete_expired_messages, diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index ed67f0335..dba61b7d9 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -47,12 +47,14 @@ start(normal, _Args) -> db_init(), start(), translate:start(), - acl:start(), ejabberd_ctl:init(), ejabberd_commands:init(), ejabberd_admin:start(), gen_mod:start(), ejabberd_config:start(), + set_loglevel_from_config(), + acl:start(), + shaper:start(), connect_nodes(), Sup = ejabberd_sup:start_link(), ejabberd_rdbms:start(), @@ -119,7 +121,7 @@ db_init() -> start_modules() -> lists:foreach( fun(Host) -> - Modules = ejabberd_config:get_local_option( + Modules = ejabberd_config:get_option( {modules, Host}, fun(Mods) -> lists:map( @@ -137,7 +139,7 @@ start_modules() -> stop_modules() -> lists:foreach( fun(Host) -> - Modules = ejabberd_config:get_local_option( + Modules = ejabberd_config:get_option( {modules, Host}, fun(Mods) -> lists:map( @@ -152,7 +154,7 @@ stop_modules() -> end, ?MYHOSTS). connect_nodes() -> - Nodes = ejabberd_config:get_local_option( + Nodes = ejabberd_config:get_option( cluster_nodes, fun(Ns) -> true = lists:all(fun is_atom/1, Ns), @@ -212,9 +214,17 @@ delete_pid_file() -> file:delete(PidFilename) end. +set_loglevel_from_config() -> + Level = ejabberd_config:get_option( + loglevel, + fun(P) when P>=0, P=<5 -> P end, + 4), + ejabberd_logger:set(Level). + start_apps() -> ejabberd:start_app(sasl), ejabberd:start_app(ssl), + ejabberd:start_app(p1_yaml), ejabberd:start_app(p1_tls), ejabberd:start_app(p1_xml), ejabberd:start_app(p1_stringprep), diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 7cc40ae1e..704c82cd4 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -423,7 +423,7 @@ auth_modules() -> %% Return the list of authenticated modules for a given host auth_modules(Server) -> LServer = jlib:nameprep(Server), - Methods = ejabberd_config:get_local_option( + Methods = ejabberd_config:get_option( {auth_method, LServer}, fun(V) when is_list(V) -> true = lists:all(fun is_atom/1, V), diff --git a/src/ejabberd_auth_anonymous.erl b/src/ejabberd_auth_anonymous.erl index d8101efc4..180e9d0df 100644 --- a/src/ejabberd_auth_anonymous.erl +++ b/src/ejabberd_auth_anonymous.erl @@ -104,7 +104,7 @@ is_login_anonymous_enabled(Host) -> %% Return the anonymous protocol to use: sasl_anon|login_anon|both %% defaults to login_anon anonymous_protocol(Host) -> - ejabberd_config:get_local_option( + ejabberd_config:get_option( {anonymous_protocol, Host}, fun(sasl_anon) -> sasl_anon; (login_anon) -> login_anon; @@ -115,7 +115,7 @@ anonymous_protocol(Host) -> %% Return true if multiple connections have been allowed in the config file %% defaults to false allow_multiple_connections(Host) -> - ejabberd_config:get_local_option( + ejabberd_config:get_option( {allow_multiple_connections, Host}, fun(V) when is_boolean(V) -> V end, false). diff --git a/src/ejabberd_auth_external.erl b/src/ejabberd_auth_external.erl index 2d1bb7cb9..514b551d7 100644 --- a/src/ejabberd_auth_external.erl +++ b/src/ejabberd_auth_external.erl @@ -48,7 +48,7 @@ %%% API %%%---------------------------------------------------------------------- start(Host) -> - Cmd = ejabberd_config:get_local_option( + Cmd = ejabberd_config:get_option( {extauth_program, Host}, fun(V) -> binary_to_list(iolist_to_binary(V)) @@ -171,7 +171,7 @@ remove_user(User, Server, Password) -> %% @spec (Host::string()) -> false | {true, CacheTime::integer()} get_cache_option(Host) -> - case ejabberd_config:get_local_option( + case ejabberd_config:get_option( {extauth_cache, Host}, fun(I) when is_integer(I), I > 0 -> I end) of undefined -> false; diff --git a/src/ejabberd_auth_internal.erl b/src/ejabberd_auth_internal.erl index 5a6c1b10a..8d7d0a0dd 100644 --- a/src/ejabberd_auth_internal.erl +++ b/src/ejabberd_auth_internal.erl @@ -406,7 +406,7 @@ is_scrammed() -> is_option_scram() -> scram == - ejabberd_config:get_local_option({auth_password_format, ?MYNAME}, + ejabberd_config:get_option({auth_password_format, ?MYNAME}, fun(V) -> V end). maybe_alert_password_scrammed_without_option() -> diff --git a/src/ejabberd_auth_ldap.erl b/src/ejabberd_auth_ldap.erl index 1baf43887..1c1a4f313 100644 --- a/src/ejabberd_auth_ldap.erl +++ b/src/ejabberd_auth_ldap.erl @@ -369,8 +369,10 @@ parse_options(Host) -> {iolist_to_binary(U), iolist_to_binary(P)}; ({U}) -> + {iolist_to_binary(U)}; + (U) -> {iolist_to_binary(U)} - end, Us) + end, lists:flatten(Us)) end, [{<<"uid">>, <<"%u">>}]), UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp), SubFilter = eldap_utils:generate_subfilter(UIDs), diff --git a/src/ejabberd_auth_pam.erl b/src/ejabberd_auth_pam.erl index c81208494..8e744e2c4 100644 --- a/src/ejabberd_auth_pam.erl +++ b/src/ejabberd_auth_pam.erl @@ -107,13 +107,13 @@ store_type() -> external. %% Internal functions %%==================================================================== get_pam_service(Host) -> - ejabberd_config:get_local_option( + ejabberd_config:get_option( {pam_service, Host}, fun iolist_to_binary/1, <<"ejabberd">>). get_pam_userinfotype(Host) -> - ejabberd_config:get_local_option( + ejabberd_config:get_option( {pam_userinfotype, Host}, fun(username) -> username; (jid) -> jid diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 5e50d9bf9..610f22c24 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -47,7 +47,8 @@ del_aux_field/2, get_subscription/2, broadcast/4, - get_subscribed/1]). + get_subscribed/1, + transform_listen_option/2]). %% gen_fsm callbacks -export([init/1, @@ -233,11 +234,10 @@ init([{SockMod, Socket}, Opts]) -> {value, {_, XS}} -> XS; _ -> false end, - Zlib = lists:member(zlib, Opts), - StartTLS = lists:member(starttls, Opts), - StartTLSRequired = lists:member(starttls_required, - Opts), - TLSEnabled = lists:member(tls, Opts), + Zlib = proplists:get_bool(zlib, Opts), + StartTLS = proplists:get_bool(starttls, Opts), + StartTLSRequired = proplists:get_bool(starttls_required, Opts), + TLSEnabled = proplists:get_bool(tls, Opts), TLS = StartTLS orelse StartTLSRequired orelse TLSEnabled, TLSOpts1 = lists:filter(fun ({certfile, _}) -> true; @@ -682,7 +682,7 @@ wait_for_feature_request({xmlstreamelement, El}, when TLS == true, TLSEnabled == false, SockMod == gen_tcp -> TLSOpts = case - ejabberd_config:get_local_option( + ejabberd_config:get_option( {domain_certfile, StateData#state.server}, fun iolist_to_binary/1) of @@ -876,7 +876,7 @@ resource_conflict_action(U, S, R) -> R) of true -> - ejabberd_config:get_local_option( + ejabberd_config:get_option( {resource_conflict, S}, fun(setresource) -> setresource; (closeold) -> closeold; @@ -2279,7 +2279,7 @@ fsm_limit_opts(Opts) -> case lists:keysearch(max_fsm_queue, 1, Opts) of {value, {_, N}} when is_integer(N) -> [{max_queue, N}]; _ -> - case ejabberd_config:get_local_option( + case ejabberd_config:get_option( max_fsm_queue, fun(I) when is_integer(I), I > 0 -> I end) of undefined -> []; @@ -2377,3 +2377,6 @@ pack_string(String, Pack) -> {value, PackedString} -> {PackedString, Pack}; none -> {String, gb_trees:insert(String, String, Pack)} end. + +transform_listen_option(Opt, Opts) -> + [Opt|Opts]. diff --git a/src/ejabberd_c2s_config.erl b/src/ejabberd_c2s_config.erl index 4dbc48f38..2229e5ef1 100644 --- a/src/ejabberd_c2s_config.erl +++ b/src/ejabberd_c2s_config.erl @@ -34,7 +34,7 @@ %% Get first c2s configuration limitations to apply it to other c2s %% connectors. get_c2s_limits() -> - case ejabberd_config:get_local_option(listen, fun(V) -> V end) of + case ejabberd_config:get_option(listen, fun(V) -> V end) of undefined -> []; C2SFirstListen -> case lists:keysearch(ejabberd_c2s, 2, C2SFirstListen) of diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl index 7442cf73c..e9a3ea3a9 100644 --- a/src/ejabberd_captcha.erl +++ b/src/ejabberd_captcha.erl @@ -504,7 +504,7 @@ do_create_image(Key) -> end. get_prog_name() -> - case ejabberd_config:get_local_option( + case ejabberd_config:get_option( captcha_cmd, fun(FileName) -> F = iolist_to_binary(FileName), @@ -521,7 +521,7 @@ get_prog_name() -> end. get_url(Str) -> - CaptchaHost = ejabberd_config:get_local_option( + CaptchaHost = ejabberd_config:get_option( captcha_host, fun iolist_to_binary/1, <<"">>), @@ -549,7 +549,7 @@ get_transfer_protocol(PortString) -> get_captcha_transfer_protocol(PortListeners). get_port_listeners(PortNumber) -> - AllListeners = ejabberd_config:get_local_option(listen, fun(V) -> V end), + AllListeners = ejabberd_config:get_option(listen, fun(V) -> V end), lists:filter(fun ({{Port, _Ip, _Netp}, _Module1, _Opts1}) when Port == PortNumber -> @@ -579,7 +579,7 @@ get_captcha_transfer_protocol([_ | Listeners]) -> is_limited(undefined) -> false; is_limited(Limiter) -> - case ejabberd_config:get_local_option( + case ejabberd_config:get_option( captcha_limit, fun(I) when is_integer(I), I > 0 -> I end) of undefined -> false; diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index b46603270..0551b49a5 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -27,16 +27,16 @@ -module(ejabberd_config). -author('alexey@process-one.net'). --export([start/0, load_file/1, +-export([start/0, load_file/1, read_file/1, add_global_option/2, add_local_option/2, get_global_option/2, get_local_option/2, get_global_option/3, get_local_option/3, - get_option/2, get_option/3, add_option/2]). --export([get_vh_by_auth_method/1]). --export([is_file_readable/1]). --export([get_version/0, get_myhosts/0, get_mylang/0]). --export([prepare_opt_val/4]). --export([convert_table_to_binary/5]). + get_option/2, get_option/3, add_option/2, + get_vh_by_auth_method/1, is_file_readable/1, + get_version/0, get_myhosts/0, get_mylang/0, + prepare_opt_val/4, convert_table_to_binary/5, + transform_options/1, collect_options/1, + convert_to_yaml/1, convert_to_yaml/2]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -53,21 +53,29 @@ start() -> + case catch mnesia:table_info(local_config, storage_type) of + disc_copies -> + mnesia:delete_table(local_config); + _ -> + ok + end, mnesia:create_table(local_config, - [{disc_copies, [node()]}, + [{ram_copies, [node()]}, {local_content, true}, {attributes, record_info(fields, local_config)}]), mnesia:add_table_copy(local_config, node(), ram_copies), Config = get_ejabberd_config_path(), - load_file(Config), + State = read_file(Config), %% This start time is used by mod_last: - add_option(node_start, now()), - ok. + {MegaSecs, Secs, _} = now(), + UnixTime = MegaSecs*1000000 + Secs, + State1 = set_option({node_start, global}, UnixTime, State), + set_opts(State1). %% @doc Get the filename of the ejabberd configuration file. -%% The filename can be specified with: erl -config "/path/to/ejabberd.cfg". +%% The filename can be specified with: erl -config "/path/to/ejabberd.yml". %% It can also be specified with the environtment variable EJABBERD_CONFIG_PATH. -%% If not specified, the default value 'ejabberd.cfg' is assumed. +%% If not specified, the default value 'ejabberd.yml' is assumed. %% @spec () -> string() get_ejabberd_config_path() -> case application:get_env(config) of @@ -81,16 +89,59 @@ get_ejabberd_config_path() -> end end. -%% @doc Load the ejabberd configuration file. +%% @doc Read the ejabberd configuration file. %% It also includes additional configuration files and replaces macros. %% This function will crash if finds some error in the configuration file. -%% @spec (File::string()) -> ok -load_file(File) -> - Terms = get_plain_terms_file(File), +%% @spec (File::string()) -> #state{}. +read_file(File) -> + read_file(File, [{replace_macros, true}, + {include_files, true}]). + +read_file(File, Opts) -> + Terms1 = get_plain_terms_file(File, Opts), + Terms_macros = case proplists:get_bool(replace_macros, Opts) of + true -> replace_macros(Terms1); + false -> Terms1 + end, + Terms = transform_terms(Terms_macros), 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). + {Head, Tail} = lists:partition( + fun({host_config, _}) -> false; + ({append_host_config, _}) -> false; + (_) -> true + end, Terms), + State1 = lists:foldl(fun process_term/2, State, Head ++ Tail), + State1#state{opts = compact(State1#state.opts)}. + +-spec load_file(string()) -> ok. + +load_file(File) -> + State = read_file(File), + set_opts(State). + +-spec convert_to_yaml(file:filename()) -> ok | {error, any()}. + +convert_to_yaml(File) -> + convert_to_yaml(File, stdout). + +-spec convert_to_yaml(file:filename(), + stdout | file:filename()) -> ok | {error, any()}. + +convert_to_yaml(File, Output) -> + State = read_file(File, [{include_files, false}]), + Opts = [{K, V} || #local_config{key = K, value = V} <- State#state.opts], + {GOpts, HOpts} = split_by_hosts(Opts), + NewOpts = GOpts ++ lists:map( + fun({Host, Opts1}) -> + {host_config, [{Host, Opts1}]} + end, HOpts), + Data = p1_yaml:encode(lists:reverse(NewOpts)), + case Output of + stdout -> + io:format("~s~n", [Data]); + FileName -> + file:write_file(FileName, Data) + end. %% @doc Read an ejabberd configuration file and return the terms. %% Input is an absolute or relative path to an ejabberd config file. @@ -99,22 +150,47 @@ load_file(File) -> %% and the terms in those files were included. %% @spec(string()) -> [term()] %% @spec(iolist()) -> [term()] -get_plain_terms_file(File) when is_binary(File) -> - get_plain_terms_file(binary_to_list(File)); -get_plain_terms_file(File1) -> +get_plain_terms_file(File) -> + get_plain_terms_file(File, [{include_files, true}]). + +get_plain_terms_file(File, Opts) when is_binary(File) -> + get_plain_terms_file(binary_to_list(File), Opts); +get_plain_terms_file(File1, Opts) -> File = get_absolute_path(File1), - case file:consult(File) of + case consult(File) of {ok, Terms} -> BinTerms = strings_to_binary(Terms), - include_config_files(BinTerms); - {error, {LineNumber, erl_parse, _ParseMessage} = Reason} -> - ExitText = describe_config_problem(File, Reason, LineNumber), - ?ERROR_MSG(ExitText, []), - exit_or_halt(ExitText); + case proplists:get_bool(include_files, Opts) of + true -> + include_config_files(BinTerms); + false -> + BinTerms + end; {error, Reason} -> - ExitText = describe_config_problem(File, Reason), - ?ERROR_MSG(ExitText, []), - exit_or_halt(ExitText) + ?ERROR_MSG(Reason, []), + exit_or_halt(Reason) + end. + +consult(File) -> + case filename:extension(File) of + ".yml" -> + case p1_yaml:decode_from_file(File, [plain_as_atom]) of + {ok, []} -> + {ok, []}; + {ok, [Document|_]} -> + {ok, Document}; + {error, Err} -> + {error, p1_yaml:format_error(Err)} + end; + _ -> + case file:consult(File) of + {ok, Terms} -> + {ok, Terms}; + {error, {LineNumber, erl_parse, _ParseMessage} = Reason} -> + {error, describe_config_problem(File, Reason, LineNumber)}; + {error, Reason} -> + {error, describe_config_problem(File, Reason)} + end end. %% @doc Convert configuration filename to absolute path. @@ -158,7 +234,7 @@ search_hosts(Term, State) -> add_hosts_to_option(Hosts, State) -> PrepHosts = normalize_hosts(Hosts), - add_option(hosts, PrepHosts, State#state{hosts = PrepHosts}). + set_option({hosts, global}, PrepHosts, State#state{hosts = PrepHosts}). normalize_hosts(Hosts) -> normalize_hosts(Hosts,[]). @@ -232,21 +308,37 @@ exit_or_halt(ExitText) -> %% @doc Include additional configuration files in the list of terms. %% @spec ([term()]) -> [term()] include_config_files(Terms) -> - include_config_files(Terms, []). + {FileOpts, Terms1} = + lists:mapfoldl( + fun({include_config_file, _} = T, Ts) -> + {[transform_include_option(T)], Ts}; + ({include_config_file, _, _} = T, Ts) -> + {[transform_include_option(T)], Ts}; + (T, Ts) -> + {[], [T|Ts]} + end, [], Terms), + Terms2 = lists:flatmap( + fun({File, Opts}) -> + include_config_file(File, Opts) + end, lists:flatten(FileOpts)), + Terms1 ++ Terms2. + +transform_include_option({include_config_file, File}) when is_list(File) -> + case is_string(File) of + true -> {File, []}; + false -> File + end; +transform_include_option({include_config_file, Filename}) -> + {Filename, []}; +transform_include_option({include_config_file, Filename, Options}) -> + {Filename, Options}. -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) -> +include_config_file(Filename, Options) -> 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]). + keep_only_allowed(Allow_only, Included_terms2). %% @doc Filter from the list of terms the disallowed. %% Returns a sublist of Terms without the ones which first element is @@ -308,12 +400,19 @@ split_terms_macros(Terms) -> fun(Term, {TOs, Ms}) -> case Term of {define_macro, Key, Value} -> - case is_atom(Key) and is_all_uppercase(Key) of + case is_correct_macro({Key, Value}) of true -> {TOs, Ms++[{Key, Value}]}; false -> exit({macro_not_properly_defined, Term}) end; + {define_macro, KeyVals} -> + case lists:all(fun is_correct_macro/1, KeyVals) of + true -> + {TOs, Ms ++ KeyVals}; + false -> + exit({macros_not_properly_defined, Term}) + end; Term -> {TOs ++ [Term], Ms} end @@ -321,6 +420,11 @@ split_terms_macros(Terms) -> {[], []}, Terms). +is_correct_macro({Key, _Val}) -> + is_atom(Key) and is_all_uppercase(Key); +is_correct_macro(_) -> + false. + %% @doc Recursively replace in Terms macro usages with the defined value. %% @spec (Terms, Macros) -> Terms %% Terms = [term()] @@ -328,7 +432,9 @@ split_terms_macros(Terms) -> replace([], _) -> []; replace([Term|Terms], Macros) -> - [replace_term(Term, Macros) | replace(Terms, Macros)]. + [replace_term(Term, Macros) | replace(Terms, Macros)]; +replace(Term, Macros) -> + replace_term(Term, Macros). replace_term(Key, Macros) when is_atom(Key) -> case is_all_uppercase(Key) of @@ -362,121 +468,65 @@ is_all_uppercase(Atom) -> process_term(Term, State) -> case Term of - override_global -> - State#state{override_global = true}; - override_local -> - State#state{override_local = true}; - override_acls -> - State#state{override_acls = true}; - {host_config, Host, Terms} -> - lists:foldl(fun(T, S) -> process_host_term(T, Host, S) end, - State, Terms); - {listen, Listeners} -> - Listeners2 = - lists:map( - fun({PortIP, Module, Opts}) -> - {Port, IPT, _, _, Proto, OptsClean} = - ejabberd_listener:parse_listener_portip(PortIP, Opts), - {{Port, IPT, Proto}, Module, OptsClean} - end, - Listeners), - add_option(listen, Listeners2, State); - {s2s_certfile, CertFile} -> - CertFileS = binary_to_list(CertFile), - case ejabberd_config:is_file_readable(CertFileS) of - true -> add_option(s2s_certfile, CertFile, State); - false -> - ErrorText = "There is a problem in the configuration: " - "the specified file is not readable: ", - throw({error, ErrorText ++ CertFileS}) - end; - {domain_certfile, Domain, CertFile} -> - CertFileS = binary_to_list(CertFile), - case ejabberd_config:is_file_readable(CertFileS) of - true -> add_option({domain_certfile, Domain}, CertFile, State); - false -> - ErrorText = "There is a problem in the configuration: " - "the specified file is not readable: ", - throw({error, ErrorText ++ CertFileS}) - end; - {loglevel, Loglevel} -> - ejabberd_logger:set(Loglevel), - State; + {host_config, HostTerms} -> + lists:foldl( + fun({Host, Terms}, AccState) -> + lists:foldl(fun(T, S) -> + process_host_term(T, Host, S, set) + end, AccState, Terms) + end, State, HostTerms); + {append_host_config, HostTerms} -> + lists:foldl( + fun({Host, Terms}, AccState) -> + lists:foldl(fun(T, S) -> + process_host_term(T, Host, S, append) + end, AccState, Terms) + end, State, HostTerms); _ -> - lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end, - State, [global|State#state.hosts]) + process_host_term(Term, global, State, set) end. -process_host_term(Term, Host, State) -> +process_host_term(Term, Host, State, Action) -> case Term of - {acl, ACLName, ACLData} -> - State#state{opts = - [acl:to_record(Host, ACLName, ACLData) | State#state.opts]}; - {access, RuleName, Rules} -> - add_option({access, RuleName, Host}, Rules, State); - {shaper, Name, Data} -> - add_option({shaper, Name, Host}, Data, State); - {modules, Modules} -> - add_option({modules, Host}, replace_modules(Modules), State); + {modules, Modules} when Action == set -> + set_option({modules, Host}, replace_modules(Modules), State); + {modules, Modules} when Action == append -> + append_option({modules, Host}, replace_modules(Modules), State); {host, _} -> State; {hosts, _} -> State; - {Opt, Val} -> - add_option({Opt, Host}, Val, State) - end. - -add_option(Opt, Val, State) when is_atom(Opt) -> - add_option({Opt, global}, Val, State); -add_option(Opt, Val, State) -> - 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. - -compact({OptName, Host} = Opt, Val, [], Os) -> - ?WARNING_MSG("The option '~p' is defined for the host ~p using host_config " - "before the global '~p' option. This host_config option may get overwritten.", [OptName, Host, OptName]), - [#local_config{key = Opt, value = Val}] ++ Os; -%% Traverse the list of the options already parsed -compact(Opt, Val, [O | Os1], Os2) -> - case catch O#local_config.key of - %% If the key of a local_config matches the Opt that wants to be added - Opt -> - %% Then prepend the new value to the list of old values - Os2 ++ [#local_config{key = Opt, - value = Val++O#local_config.value} - ] ++ Os1; - _ -> - compact(Opt, Val, Os1, Os2++[O]) + {Opt, Val} when Action == set -> + set_option({Opt, Host}, Val, State); + {Opt, Val} when Action == append -> + append_option({Opt, Host}, Val, State); + Opt -> + ?WARNING_MSG("Ignore invalid (outdated?) option ~p", [Opt]), + State end. +set_option(Opt, Val, State) -> + State#state{opts = [#local_config{key = Opt, value = Val} | + State#state.opts]}. + +append_option({Opt, Host}, Val, State) -> + GlobalVals = lists:flatmap( + fun(#local_config{key = {O, global}, value = V}) + when O == Opt -> + if is_list(V) -> V; + true -> [V] + end; + (_) -> + [] + end, State#state.opts), + NewVal = if is_list(Val) -> Val ++ GlobalVals; + true -> [Val|GlobalVals] + end, + set_option({Opt, Host}, NewVal, State). set_opts(State) -> - Opts = lists:reverse(State#state.opts), + Opts = State#state.opts, F = fun() -> - 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) @@ -565,11 +615,22 @@ get_option(Opt, F) -> get_option(Opt, F, Default) when is_atom(Opt) -> get_option({Opt, global}, F, Default); get_option(Opt, F, Default) -> + case Opt of + {O, global} when is_atom(O) -> ok; + {O, H} when is_atom(O), is_binary(H) -> ok; + _ -> ?WARNING_MSG("Option ~p has invalid (outdated?) format. " + "This is likely a bug", [Opt]) + end, case ets:lookup(local_config, Opt) of [#local_config{value = Val}] -> prepare_opt_val(Opt, Val, F, Default); - _ -> - Default + _ -> + case Opt of + {Key, Host} when Host /= global -> + get_option({Key, global}, F, Default); + _ -> + Default + end end. -spec get_vh_by_auth_method(atom()) -> [binary()]. @@ -632,14 +693,14 @@ replace_modules(Modules) -> emit_deprecation_warning(Module, NewModule, DBType), NewOpts = [{db_type, DBType} | lists:keydelete(db_type, 1, Opts)], - {NewModule, NewOpts}; + {NewModule, transform_module_options(Module, NewOpts)}; NewModule -> if Module /= NewModule -> emit_deprecation_warning(Module, NewModule); true -> ok end, - {NewModule, Opts} + {NewModule, transform_module_options(Module, Opts)} end end, Modules). @@ -695,6 +756,142 @@ format_term(S) when is_list(S), S /= [] -> format_term(T) -> io_lib:format("~p", [binary_to_strings(T)]). +transform_terms(Terms) -> + %% We could check all ejabberd beams, but this + %% slows down start-up procedure :( + Mods = [mod_register, + mod_last, + ejabberd_s2s, + ejabberd_listener, + ejabberd_odbc_sup, + shaper, + ejabberd_s2s_out, + acl, + ejabberd_config], + collect_options(transform_terms(Mods, Terms)). + +transform_terms([Mod|Mods], Terms) -> + case catch Mod:transform_options(Terms) of + {'EXIT', _} = Err -> + ?ERROR_MSG("Failed to transform terms by ~p: ~p", [Mod, Err]), + transform_terms(Mods, Terms); + NewTerms -> + transform_terms(Mods, NewTerms) + end; +transform_terms([], NewTerms) -> + NewTerms. + +transform_module_options(Module, Opts) -> + Opts1 = gen_iq_handler:transform_module_options(Opts), + try + Module:transform_module_options(Opts1) + catch error:undef -> + Opts1 + end. + +compact(Cfg) -> + Opts = [{K, V} || #local_config{key = K, value = V} <- Cfg], + {GOpts, HOpts} = split_by_hosts(Opts), + [#local_config{key = {O, global}, value = V} || {O, V} <- GOpts] ++ + lists:flatmap( + fun({Host, OptVal}) -> + case lists:member(OptVal, GOpts) of + true -> + []; + false -> + [#local_config{key = {Opt, Host}, value = Val} + || {Opt, Val} <- OptVal] + end + end, lists:flatten(HOpts)). + +split_by_hosts(Opts) -> + Opts1 = orddict:to_list( + lists:foldl( + fun({{Opt, Host}, Val}, D) -> + orddict:append(Host, {Opt, Val}, D) + end, orddict:new(), Opts)), + case lists:keytake(global, 1, Opts1) of + {value, {global, GlobalOpts}, HostOpts} -> + {GlobalOpts, HostOpts}; + _ -> + {[], Opts1} + end. + +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_options(Opts) -> + Opts1 = lists:foldl(fun transform_options/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_terms(O)} || {H, O} <- HOs]}] + end, + AHOpts1 = case collect_options(lists:flatten(AHOpts)) of + [] -> + []; + AHOs -> + [{append_host_config, + [{H, transform_terms(O)} || {H, O} <- AHOs]}] + end, + HOpts1 ++ AHOpts1 ++ Opts3. + +transform_options({domain_certfile, Domain, CertFile}, Opts) -> + ?WARNING_MSG("Option 'domain_certfile' now should be defined " + "per virtual host or globally. The old format is " + "still supported but it is better to fix your config", []), + [{host_config, [{Domain, [{domain_certfile, CertFile}]}]}|Opts]; +transform_options(Opt, Opts) when Opt == override_global; + Opt == override_local; + Opt == override_acls -> + ?WARNING_MSG("Ignoring '~s' option which has no effect anymore", [Opt]), + Opts; +transform_options({host_config, Host, HOpts}, Opts) -> + {AddOpts, HOpts1} = + lists:mapfoldl( + fun({{add, Opt}, Val}, Os) -> + ?WARNING_MSG("Option 'add' is deprecated. " + "The option is still supported " + "but it is better to fix your config: " + "use 'append_host_config' instead.", []), + {[{Opt, Val}], Os}; + (O, Os) -> + {[], [O|Os]} + end, [], HOpts), + [{append_host_config, [{Host, lists:flatten(AddOpts)}]}, + {host_config, [{Host, HOpts1}]}|Opts]; +transform_options({define_macro, Macro, Val}, Opts) -> + [{define_macro, [{Macro, Val}]}|Opts]; +transform_options({include_config_file, _} = Opt, Opts) -> + [{include_config_file, [transform_include_option(Opt)]} | Opts]; +transform_options({include_config_file, _, _} = Opt, Opts) -> + [{include_config_file, [transform_include_option(Opt)]} | Opts]; +transform_options(Opt, Opts) -> + [Opt|Opts]. + -spec convert_table_to_binary(atom(), [atom()], atom(), fun(), fun()) -> ok. diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 38b94ef4d..d3944e266 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -237,7 +237,7 @@ process2(Args, Auth, AccessCommands) -> end. get_accesscommands() -> - ejabberd_config:get_local_option(ejabberdctl_access_commands, + ejabberd_config:get_option(ejabberdctl_access_commands, fun(V) when is_list(V) -> V end, []). %%----------------------------- diff --git a/src/ejabberd_frontend_socket.erl b/src/ejabberd_frontend_socket.erl index adc7c2579..583396ec9 100644 --- a/src/ejabberd_frontend_socket.erl +++ b/src/ejabberd_frontend_socket.erl @@ -280,7 +280,7 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}. %%% Internal functions %%-------------------------------------------------------------------- check_starttls(SockMod, Socket, Receiver, Opts) -> - TLSEnabled = lists:member(tls, Opts), + TLSEnabled = proplists:get_bool(tls, Opts), TLSOpts = lists:filter(fun({certfile, _}) -> true; (_) -> false end, Opts), @@ -292,4 +292,3 @@ check_starttls(SockMod, Socket, Receiver, Opts) -> true -> {SockMod, Socket} end. - diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index 899d50eb3..151a34ef4 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -30,7 +30,8 @@ %% External exports -export([start/2, start_link/2, become_controller/1, - socket_type/0, receive_headers/1, url_encode/1]). + socket_type/0, receive_headers/1, url_encode/1, + transform_listen_option/2]). %% Callbacks -export([init/2]). @@ -91,7 +92,7 @@ start_link(SockData, Opts) -> [SockData, Opts])}. init({SockMod, Socket}, Opts) -> - TLSEnabled = lists:member(tls, Opts), + TLSEnabled = proplists:get_bool(tls, Opts), TLSOpts1 = lists:filter(fun ({certfile, _}) -> true; (_) -> false end, @@ -133,12 +134,13 @@ init({SockMod, Socket}, Opts) -> true -> [{[<<"http-poll">>], ejabberd_http_poll}]; false -> [] end, - DefinedHandlers = case lists:keysearch(request_handlers, - 1, Opts) - of - {value, {request_handlers, H}} -> H; - false -> [] - end, + DefinedHandlers = gen_mod:get_opt( + request_handlers, Opts, + fun(Hs) -> + [{str:tokens( + iolist_to_binary(Path), <<"/">>), + Mod} || {Path, Mod} <- Hs] + end, []), RequestHandlers = DefinedHandlers ++ Captcha ++ Register ++ Admin ++ Bind ++ Poll, ?DEBUG("S: ~p~n", [RequestHandlers]), @@ -484,7 +486,7 @@ analyze_ip_xff(IP, [], _Host) -> IP; analyze_ip_xff({IPLast, Port}, XFF, Host) -> [ClientIP | ProxiesIPs] = str:tokens(XFF, <<", ">>) ++ [jlib:ip_to_list(IPLast)], - TrustedProxies = ejabberd_config:get_local_option( + TrustedProxies = ejabberd_config:get_option( {trusted_proxies, Host}, fun(TPs) -> [iolist_to_binary(TP) || TP <- TPs] @@ -834,3 +836,25 @@ normalize_path([_Parent, <<"..">>|Path], Norm) -> normalize_path(Path, Norm); normalize_path([Part | Path], Norm) -> normalize_path(Path, [Part|Norm]). + +transform_listen_option(captcha, Opts) -> + [{captcha, true}|Opts]; +transform_listen_option(register, Opts) -> + [{register, true}|Opts]; +transform_listen_option(web_admin, Opts) -> + [{web_admin, true}|Opts]; +transform_listen_option(http_bind, Opts) -> + [{http_bind, true}|Opts]; +transform_listen_option(http_poll, Opts) -> + [{http_poll, true}|Opts]; +transform_listen_option({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(Opt, Opts) -> + [Opt|Opts]. diff --git a/src/ejabberd_http_poll.erl b/src/ejabberd_http_poll.erl index f144aeb59..89933a595 100644 --- a/src/ejabberd_http_poll.erl +++ b/src/ejabberd_http_poll.erl @@ -205,7 +205,7 @@ get_human_html_xmlel() -> init([ID, Key, IP]) -> ?INFO_MSG("started: ~p", [{ID, Key, IP}]), Opts = ejabberd_c2s_config:get_c2s_limits(), - HTTPPollTimeout = ejabberd_config:get_local_option( + HTTPPollTimeout = ejabberd_config:get_option( {http_poll_timeout, ?MYNAME}, fun(I) when is_integer(I), I>0 -> I end, ?HTTP_POLL_TIMEOUT) * 1000, diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index be90cf92f..033eb0e0f 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -36,7 +36,8 @@ parse_listener_portip/2, add_listener/3, delete_listener/2, - validate_cfg/1 + transform_options/1, + validate_cfg/1 ]). -include("ejabberd.hrl"). @@ -55,7 +56,7 @@ init(_) -> {ok, {{one_for_one, 10, 1}, []}}. bind_tcp_ports() -> - case ejabberd_config:get_local_option(listen, fun validate_cfg/1) of + case ejabberd_config:get_option(listen, fun validate_cfg/1) of undefined -> ignore; Ls -> @@ -88,7 +89,7 @@ bind_tcp_port(PortIP, Module, RawOpts) -> end. start_listeners() -> - case ejabberd_config:get_local_option(listen, fun validate_cfg/1) of + case ejabberd_config:get_option(listen, fun validate_cfg/1) of undefined -> ignore; Ls -> @@ -267,7 +268,7 @@ strip_ip_option(Opts) -> Opts), case IPL of %% Only the first ip option is considered - [{ip, T1} | _] when is_tuple(T1) -> + [{ip, T1} | _] -> {T1, OptsNoIP}; [] -> {no_ip_option, OptsNoIP} @@ -364,7 +365,7 @@ start_listener_sup(Port, Module, Opts) -> supervisor:start_child(ejabberd_listeners, ChildSpec). stop_listeners() -> - Ports = ejabberd_config:get_local_option(listen, fun validate_cfg/1), + Ports = ejabberd_config:get_option(listen, fun validate_cfg/1), lists:foreach( fun({PortIpNetp, Module, _Opts}) -> delete_listener(PortIpNetp, Module) @@ -397,7 +398,7 @@ add_listener(PortIP, Module, Opts) -> PortIP1 = {Port, IPT, Proto}, case start_listener(PortIP1, Module, Opts) of {ok, _Pid} -> - Ports = case ejabberd_config:get_local_option( + Ports = case ejabberd_config:get_option( listen, fun validate_cfg/1) of undefined -> []; @@ -406,7 +407,8 @@ add_listener(PortIP, Module, Opts) -> end, Ports1 = lists:keydelete(PortIP1, 1, Ports), Ports2 = [{PortIP1, Module, Opts} | Ports1], - ejabberd_config:add_local_option(listen, Ports2), + Ports3 = lists:map(fun transform_option/1, Ports2), + ejabberd_config:add_option(listen, Ports3), ok; {error, {already_started, _Pid}} -> {error, {already_started, PortIP}}; @@ -428,7 +430,7 @@ delete_listener(PortIP, Module) -> delete_listener(PortIP, Module, Opts) -> {Port, IPT, _, _, Proto, _} = parse_listener_portip(PortIP, Opts), PortIP1 = {Port, IPT, Proto}, - Ports = case ejabberd_config:get_local_option( + Ports = case ejabberd_config:get_option( listen, fun validate_cfg/1) of undefined -> []; @@ -436,7 +438,8 @@ delete_listener(PortIP, Module, Opts) -> Ls end, Ports1 = lists:keydelete(PortIP1, 1, Ports), - ejabberd_config:add_local_option(listen, Ports1), + Ports2 = lists:map(fun transform_option/1, Ports1), + ejabberd_config:add_option(listen, Ports2), stop_listener(PortIP1, Module). @@ -541,6 +544,55 @@ format_error(Reason) -> -define(IS_PORT(P), (is_integer(P) and (P > 0) and (P =< 65535))). -define(IS_TRANSPORT(T), ((T == tcp) or (T == udp))). +transform_option({{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))}; + (tls) -> {tls, true}; + (ssl) -> {tls, true}; + (zlib) -> {zlib, true}; + (starttls) -> {starttls, true}; + (starttls_required) -> {starttls_required, true}; + (Opt) -> Opt + end, Opts), + Opts2 = lists:foldl( + fun(Opt, Acc) -> + try + Mod:transform_listen_option(Opt, Acc) + catch error:undef -> + Acc + end + 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_option({{Port, Transport}, Mod, Opts}) + when ?IS_TRANSPORT(Transport) -> + transform_option({{Port, {0,0,0,0}, Transport}, Mod, Opts}); +transform_option({{Port, IP}, Mod, Opts}) -> + transform_option({{Port, IP, tcp}, Mod, Opts}); +transform_option({Port, Mod, Opts}) -> + transform_option({{Port, {0,0,0,0}, tcp}, Mod, Opts}); +transform_option(Opt) -> + Opt. + +transform_options(Opts) -> + lists:foldl(fun transform_options/2, [], Opts). + +transform_options({listen, LOpts}, Opts) -> + [{listen, lists:map(fun transform_option/1, LOpts)} | Opts]; +transform_options(Opt, Opts) -> + [Opt|Opts]. + -type transport() :: udp | tcp. -type port_ip_transport() :: inet:port_number() | {inet:port_number(), transport()} | @@ -551,18 +603,21 @@ format_error(Reason) -> validate_cfg(L) -> lists:map( - fun({PortIPTransport, Mod1, Opts}) when is_atom(Mod1), is_list(Opts) -> - Mod = prepare_mod(Mod1), - case PortIPTransport of - Port when ?IS_PORT(Port) -> - {Port, Mod, Opts}; - {Port, Trans} when ?IS_PORT(Port) and ?IS_TRANSPORT(Trans) -> - {{Port, Trans}, Mod, Opts}; - {Port, IP} when ?IS_PORT(Port) -> - {{Port, prepare_ip(IP)}, Mod, Opts}; - {Port, IP, Trans} when ?IS_PORT(Port) and ?IS_TRANSPORT(Trans) -> - {{Port, prepare_ip(IP), Trans}, Mod, Opts} - end + fun(LOpts) -> + lists:foldl( + fun({port, Port}, {{_, IP, T}, Mod, Opts}) -> + true = ?IS_PORT(Port), + {{Port, IP, T}, Mod, Opts}; + ({ip, IP}, {{Port, _, T}, Mod, Opts}) -> + {{Port, prepare_ip(IP), T}, Mod, Opts}; + ({transport, T}, {{Port, IP, _}, Mod, Opts}) -> + true = ?IS_TRANSPORT(T), + {{Port, IP, T}, Mod, Opts}; + ({module, Mod}, {Port, _, Opts}) -> + {Port, prepare_mod(Mod), Opts}; + (Opt, {Port, Mod, Opts}) -> + {Port, Mod, [Opt|Opts]} + end, {{5222, {0,0,0,0}, tcp}, ejabberd_c2s, []}, LOpts) end, L). prepare_ip({A, B, C, D} = IP) @@ -583,5 +638,5 @@ prepare_mod(ejabberd_stun) -> prepare_mod(stun) -> ejabberd:start_app(p1_stun), stun; -prepare_mod(Mod) -> +prepare_mod(Mod) when is_atom(Mod) -> Mod. diff --git a/src/ejabberd_node_groups.erl b/src/ejabberd_node_groups.erl index 4e0335b97..eb39b7265 100644 --- a/src/ejabberd_node_groups.erl +++ b/src/ejabberd_node_groups.erl @@ -84,7 +84,12 @@ get_closest_node(Name) -> %%-------------------------------------------------------------------- init([]) -> {FE, BE} = - case ejabberd_config:get_local_option(node_type, fun(N) -> N end) of + case ejabberd_config:get_option( + node_type, + fun(frontend) -> frontend; + (backend) -> backend; + (generic) -> generic + end, generic) of frontend -> {true, false}; backend -> diff --git a/src/ejabberd_odbc.erl b/src/ejabberd_odbc.erl index 937805579..40a15a5ce 100644 --- a/src/ejabberd_odbc.erl +++ b/src/ejabberd_odbc.erl @@ -71,12 +71,12 @@ -define(TOP_LEVEL_TXN, 0). --define(MAX_TRANSACTION_RESTARTS, 10). - -define(PGSQL_PORT, 5432). -define(MYSQL_PORT, 3306). +-define(MAX_TRANSACTION_RESTARTS, 10). + -define(TRANSACTION_TIMEOUT, 60000). -define(KEEPALIVE_TIMEOUT, 60000). @@ -201,8 +201,8 @@ decode_term(Bin) -> %%% Callback functions from gen_fsm %%%---------------------------------------------------------------------- init([Host, StartInterval]) -> - case ejabberd_config:get_local_option( - {odbc_keepalive_interval, Host}, + case ejabberd_config:get_option( + {keepalive_interval, Host}, fun(I) when is_integer(I), I>0 -> I end) of undefined -> ok; @@ -573,39 +573,39 @@ log(Level, Format, Args) -> end. db_opts(Host) -> - case ejabberd_config:get_local_option( - {odbc_server, Host}, - fun({Type, Server, DB, User, Pass}) -> - {Type, - iolist_to_binary(Server), - case Type of - mysql -> ?MYSQL_PORT; - pgsql -> ?PGSQL_PORT - end, - iolist_to_binary(DB), - iolist_to_binary(User), - iolist_to_binary(Pass)}; - ({Type, Server, Port, DB, User, Pass}) - when ((Type == mysql) or (Type == pgsql)) - and (is_integer(Port) and ((Port > 0) - and (Port < 65536))) -> - {Type, - iolist_to_binary(Server), - Port, - iolist_to_binary(DB), - iolist_to_binary(User), - iolist_to_binary(Pass)}; - (S) -> - iolist_to_binary(S) - end, <<"localhost">>) of - {Type, Server, Port, DB, User, Pass} -> - [Type, Server, Port, DB, User, Pass]; - SQLServer -> - [odbc, SQLServer] + Type = ejabberd_config:get_option({odbc_type, Host}, + fun(mysql) -> mysql; + (pgsql) -> pgsql; + (odbc) -> odbc + end, odbc), + Server = ejabberd_config:get_option({odbc_server, Host}, + fun iolist_to_binary/1, + <<"localhost">>), + case Type of + odbc -> + [odbc, Server]; + _ -> + Port = ejabberd_config:get_option( + {port, Host}, + fun(P) when is_integer(P), P > 0, P < 65536 -> P end, + case Type of + mysql -> ?MYSQL_PORT; + pgsql -> ?PGSQL_PORT + end), + DB = ejabberd_config:get_option({odbc_database, Host}, + fun iolist_to_binary/1, + <<"ejabberd">>), + User = ejabberd_config:get_option({odbc_username, Host}, + fun iolist_to_binary/1, + <<"ejabberd">>), + Pass = ejabberd_config:get_option({odbc_password, Host}, + fun iolist_to_binary/1, + <<"">>), + [Type, Server, Port, DB, User, Pass] end. max_fsm_queue() -> - ejabberd_config:get_local_option( + ejabberd_config:get_option( max_fsm_queue, fun(N) when is_integer(N), N > 0 -> N end). diff --git a/src/ejabberd_odbc_sup.erl b/src/ejabberd_odbc_sup.erl index 2ddc751f8..86b737948 100644 --- a/src/ejabberd_odbc_sup.erl +++ b/src/ejabberd_odbc_sup.erl @@ -30,11 +30,15 @@ %% API -export([start_link/1, init/1, add_pid/2, remove_pid/2, - get_pids/1, get_random_pid/1]). + get_pids/1, get_random_pid/1, transform_options/1]). -include("ejabberd.hrl"). -include("logger.hrl"). +-define(PGSQL_PORT, 5432). + +-define(MYSQL_PORT, 3306). + -define(DEFAULT_POOL_SIZE, 10). -define(DEFAULT_ODBC_START_INTERVAL, 30). @@ -56,11 +60,11 @@ start_link(Host) -> ?MODULE, [Host]). init([Host]) -> - PoolSize = ejabberd_config:get_local_option( + PoolSize = ejabberd_config:get_option( {odbc_pool_size, Host}, fun(I) when is_integer(I), I>0 -> I end, ?DEFAULT_POOL_SIZE), - StartInterval = ejabberd_config:get_local_option( + StartInterval = ejabberd_config:get_option( {odbc_start_interval, Host}, fun(I) when is_integer(I), I>0 -> I end, ?DEFAULT_ODBC_START_INTERVAL), @@ -93,3 +97,20 @@ remove_pid(Host, Pid) -> mnesia:delete_object(#sql_pool{host = Host, pid = Pid}) end, mnesia:ets(F). + +transform_options(Opts) -> + lists:foldl(fun transform_options/2, [], Opts). + +transform_options({odbc_server, {Type, Server, Port, DB, User, Pass}}, Opts) -> + [{odbc_type, Type}, + {odbc_server, Server}, + {odbc_port, Port}, + {odbc_database, DB}, + {odbc_username, User}, + {odbc_password, Pass}|Opts]; +transform_options({odbc_server, {mysql, Server, DB, User, Pass}}, Opts) -> + transform_options({odbc_server, {mysql, Server, ?MYSQL_PORT, DB, User, Pass}}, Opts); +transform_options({odbc_server, {pgsql, Server, DB, User, Pass}}, Opts) -> + transform_options({odbc_server, {pgsql, Server, ?PGSQL_PORT, DB, User, Pass}}, Opts); +transform_options(Opt, Opts) -> + [Opt|Opts]. diff --git a/src/ejabberd_rdbms.erl b/src/ejabberd_rdbms.erl index a7324e4ca..b5525de10 100644 --- a/src/ejabberd_rdbms.erl +++ b/src/ejabberd_rdbms.erl @@ -69,18 +69,16 @@ start_odbc(Host, App) -> start_odbc(Host, App) end. -%% Returns {true, App} if we have configured odbc_server for the given host +%% Returns {true, App} if we have configured odbc for the given host needs_odbc(Host) -> LHost = jlib:nameprep(Host), - case ejabberd_config:get_local_option( - {odbc_server, LHost}, fun(Res) -> Res end) of - {mysql, _, _, _, _} -> {true, p1_mysql}; - {pgsql, _, _, _, _} -> {true, p1_pgsql}; - {mysql, _, _, _, _, _} -> {true, p1_mysql}; - {pgsql, _, _, _, _, _} -> {true, p1_pgsql}; - S -> - case catch iolist_to_binary(S) of - {'EXIT', _} -> false; - _ -> true - end + case ejabberd_config:get_option({odbc_type, LHost}, + fun(mysql) -> mysql; + (pgsql) -> pgsql; + (odbc) -> odbc + end, undefined) of + mysql -> {true, p1_mysql}; + pgsql -> {true, p1_pgsql}; + odbc -> {true, odbc}; + undefined -> false end. diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index ea020a186..24f52d329 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -387,14 +387,10 @@ do_route(OrigFrom, OrigTo, OrigPacket) -> end. get_component_number(LDomain) -> - case - ejabberd_config:get_local_option({domain_balancing_component_number, - LDomain}, fun(D) -> D end) - of - N when is_integer(N), N > 1 -> N; - _ -> undefined - end. - + ejabberd_config:get_option( + {domain_balancing_component_number, LDomain}, + fun(N) when is_integer(N), N > 1 -> N end, + undefined). update_tables() -> case catch mnesia:table_info(route, attributes) of diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index 591d31da2..b13fdd58e 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -45,7 +45,7 @@ handle_info/2, terminate/2, code_change/3]). %% ejabberd API --export([get_info_s2s_connections/1]). +-export([get_info_s2s_connections/1, transform_options/1]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -461,12 +461,12 @@ needed_connections_number(Ls, MaxS2SConnectionsNumber, %% -------------------------------------------------------------------- is_service(From, To) -> LFromDomain = From#jid.lserver, - case ejabberd_config:get_local_option( + case ejabberd_config:get_option( {route_subdomains, LFromDomain}, - fun(s2s) -> s2s end) of + fun(s2s) -> s2s; (local) -> local end, local) of s2s -> % bypass RFC 3920 10.3 false; - undefined -> + local -> Hosts = (?MYHOSTS), P = fun (ParentDomain) -> lists:member(ParentDomain, Hosts) @@ -548,34 +548,50 @@ allow_host2(MyServer, S2SHost) -> end. allow_host1(MyHost, S2SHost) -> - case ejabberd_config:get_local_option( - {{s2s_host, S2SHost}, MyHost}, - fun(deny) -> deny; (allow) -> allow end) - of - deny -> false; - allow -> true; - undefined -> - case ejabberd_config:get_local_option( - {s2s_default_policy, MyHost}, - fun(deny) -> deny; (allow) -> allow end) - of - deny -> false; - _ -> - case ejabberd_hooks:run_fold(s2s_allow_host, MyHost, - allow, [MyHost, S2SHost]) - of - deny -> false; - allow -> true; - _ -> true - end - end + Rule = ejabberd_config:get_option( + s2s_access, + fun(A) when is_atom(A) -> A end, + all), + JID = jlib:make_jid(<<"">>, S2SHost, <<"">>), + case acl:match_rule(MyHost, Rule, JID) of + deny -> false; + allow -> + case ejabberd_hooks:run_fold(s2s_allow_host, MyHost, + allow, [MyHost, S2SHost]) of + deny -> false; + allow -> true; + _ -> true + end end. +transform_options(Opts) -> + lists:foldl(fun transform_options/2, [], Opts). + +transform_options({{s2s_host, Host}, Action}, Opts) -> + ?WARNING_MSG("Option 's2s_host' is deprecated. " + "The option is still supported but it is better to " + "fix your config: use access rules instead.", []), + ACLName = jlib:binary_to_atom( + iolist_to_binary(["s2s_access_", Host])), + [{acl, ACLName, {server, Host}}, + {access, s2s, [{Action, ACLName}]}, + {s2s_access, s2s} | + Opts]; +transform_options({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_options(Opt, Opts) -> + [Opt|Opts]. + %% Get information about S2S connections of the specified type. %% @spec (Type) -> [Info] %% where Type = in | out %% Info = [{InfoName::atom(), InfoValue::any()}] - get_info_s2s_connections(Type) -> ChildType = case Type of in -> ejabberd_s2s_in_sup; diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl index 7692614ea..2ad335657 100644 --- a/src/ejabberd_s2s_in.erl +++ b/src/ejabberd_s2s_in.erl @@ -149,7 +149,7 @@ init([{SockMod, Socket}, Opts]) -> _ -> none end, {StartTLS, TLSRequired, TLSCertverify} = - case ejabberd_config:get_local_option( + case ejabberd_config:get_option( s2s_use_starttls, fun(false) -> false; (true) -> true; @@ -171,7 +171,7 @@ init([{SockMod, Socket}, Opts]) -> required_trusted -> {true, true, true} end, - TLSOpts1 = case ejabberd_config:get_local_option( + TLSOpts1 = case ejabberd_config:get_option( s2s_certfile, fun iolist_to_binary/1) of undefined -> []; @@ -324,7 +324,7 @@ wait_for_feature_request({xmlstreamelement, El}, ?DEBUG("starttls", []), Socket = StateData#state.socket, TLSOpts1 = case - ejabberd_config:get_local_option( + ejabberd_config:get_option( {domain_certfile, StateData#state.server}, fun iolist_to_binary/1) of undefined -> StateData#state.tls_options; @@ -332,7 +332,7 @@ wait_for_feature_request({xmlstreamelement, El}, [{certfile, CertFile} | lists:keydelete(certfile, 1, StateData#state.tls_options)] end, - TLSOpts = case ejabberd_config:get_local_option( + TLSOpts = case ejabberd_config:get_option( {s2s_tls_compression, StateData#state.server}, fun(true) -> true; (false) -> false @@ -843,7 +843,7 @@ fsm_limit_opts(Opts) -> case lists:keysearch(max_fsm_queue, 1, Opts) of {value, {_, N}} when is_integer(N) -> [{max_queue, N}]; _ -> - case ejabberd_config:get_local_option( + case ejabberd_config:get_option( max_fsm_queue, fun(I) when is_integer(I), I > 0 -> I end) of undefined -> []; diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index 1aaaa9998..f52a673e4 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -35,7 +35,8 @@ start_link/3, start_connection/1, terminate_if_waiting_delay/2, - stop_connection/1]). + stop_connection/1, + transform_options/1]). %% p1_fsm callbacks (same as gen_fsm) -export([init/1, @@ -161,7 +162,7 @@ init([From, Server, Type]) -> process_flag(trap_exit, true), ?DEBUG("started: ~p", [{From, Server, Type}]), {TLS, TLSRequired} = case - ejabberd_config:get_local_option( + ejabberd_config:get_option( s2s_use_starttls, fun(true) -> true; (false) -> false; @@ -184,13 +185,13 @@ init([From, Server, Type]) -> end, UseV10 = TLS, TLSOpts1 = case - ejabberd_config:get_local_option( + ejabberd_config:get_option( s2s_certfile, fun iolist_to_binary/1) of undefined -> [connect]; CertFile -> [{certfile, CertFile}, connect] end, - TLSOpts = case ejabberd_config:get_local_option( + TLSOpts = case ejabberd_config:get_option( {s2s_tls_compression, From}, fun(true) -> true; (false) -> false @@ -702,7 +703,7 @@ wait_for_starttls_proceed({xmlstreamelement, El}, [{StateData#state.myname, StateData#state.server}]), Socket = StateData#state.socket, TLSOpts = case - ejabberd_config:get_local_option( + ejabberd_config:get_option( {domain_certfile, StateData#state.myname}, fun iolist_to_binary/1) of @@ -1142,16 +1143,15 @@ get_addr_port(Server) -> end. srv_lookup(Server) -> - Options = case - ejabberd_config:get_local_option( - s2s_dns_options, fun(L) when is_list(L) -> L end) - of - undefined -> []; - L -> L - end, - TimeoutMs = timer:seconds(proplists:get_value(timeout, - Options, 10)), - Retries = proplists:get_value(retries, Options, 2), + TimeoutMs = timer:seconds( + ejabberd_config:get_option( + s2s_dns_timeout, + fun(I) when is_integer(I), I>=0 -> I end, + 10)), + Retries = ejabberd_config:get_option( + s2s_dns_retries, + fun(I) when is_integer(I), I>=0 -> I end, + 2), srv_lookup(binary_to_list(Server), TimeoutMs, Retries). %% XXX - this behaviour is suboptimal in the case that the domain @@ -1211,15 +1211,15 @@ get_addrs(Host, Family) -> end. outgoing_s2s_port() -> - ejabberd_config:get_local_option( + ejabberd_config:get_option( outgoing_s2s_port, fun(I) when is_integer(I), I > 0, I =< 65536 -> I end, 5269). outgoing_s2s_families() -> - ejabberd_config:get_local_option( - outgoing_s2s_options, - fun({Families, _}) -> + ejabberd_config:get_option( + outgoing_s2s_families, + fun(Families) -> true = lists:all( fun(ipv4) -> true; (ipv6) -> true @@ -1228,14 +1228,43 @@ outgoing_s2s_families() -> end, [ipv4, ipv6]). outgoing_s2s_timeout() -> - ejabberd_config:get_local_option( - outgoing_s2s_options, - fun({_, TimeOut}) when is_integer(TimeOut), TimeOut > 0 -> + ejabberd_config:get_option( + outgoing_s2s_timeout, + fun(TimeOut) when is_integer(TimeOut), TimeOut > 0 -> TimeOut; - ({_, infinity}) -> + (infinity) -> infinity end, 10000). +transform_options(Opts) -> + lists:foldl(fun transform_options/2, [], Opts). + +transform_options({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_options({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_options(Opt, Opts) -> + [Opt|Opts]. + %% Human readable S2S logging: Log only new outgoing connections as INFO %% Do not log dialback log_s2s_out(false, _, _, _) -> ok; @@ -1278,7 +1307,7 @@ wait_before_reconnect(StateData) -> queue = queue:new()}}. get_max_retry_delay() -> - case ejabberd_config:get_local_option( + case ejabberd_config:get_option( s2s_max_retry_delay, fun(I) when is_integer(I), I > 0 -> I end) of undefined -> ?MAX_RETRY_DELAY; @@ -1295,7 +1324,7 @@ terminate_if_waiting_delay(From, To) -> Pids). fsm_limit_opts() -> - case ejabberd_config:get_local_option( + case ejabberd_config:get_option( max_fsm_queue, fun(I) when is_integer(I), I > 0 -> I end) of undefined -> []; diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl index 49bea8950..35f7c187a 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -34,7 +34,7 @@ %% External exports -export([start/2, start_link/2, send_text/2, - send_element/2, socket_type/0]). + send_element/2, socket_type/0, transform_listen_option/2]). %% gen_fsm callbacks -export([init/1, wait_for_stream/2, @@ -124,29 +124,18 @@ init([{SockMod, Socket}, Opts]) -> {value, {_, A}} -> A; _ -> all end, - {Hosts, Password} = case lists:keysearch(hosts, 1, Opts) - of - {value, {_, Hs, HOpts}} -> - case lists:keysearch(password, 1, HOpts) of - {value, {_, P}} -> {Hs, P}; - _ -> - % TODO: generate error - false - end; - _ -> - case lists:keysearch(host, 1, Opts) of - {value, {_, H, HOpts}} -> - case lists:keysearch(password, 1, HOpts) of - {value, {_, P}} -> {[H], P}; - _ -> - % TODO: generate error - false - end; - _ -> - % TODO: generate error - false - end - end, + %% This should be improved probably + {Hosts, HostOpts} = case lists:keyfind(hosts, 1, Opts) of + {_, HOpts} -> + {[H || {H, _} <- HOpts], + lists:flatten( + [O || {_, O} <- HOpts])}; + _ -> + {[], []} + end, + Password = gen_mod:get_opt(password, HostOpts, + fun iolist_to_binary/1, + p1_sha:sha(crypto:rand_bytes(20))), Shaper = case lists:keysearch(shaper_rule, 1, Opts) of {value, {_, S}} -> S; _ -> none @@ -384,12 +373,30 @@ send_element(StateData, El) -> new_id() -> randoms:get_string(). +transform_listen_option({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({host, Host, Os}, Opts) -> + transform_listen_option({hosts, [Host], Os}, Opts); +transform_listen_option(Opt, Opts) -> + [Opt|Opts]. + fsm_limit_opts(Opts) -> case lists:keysearch(max_fsm_queue, 1, Opts) of {value, {_, N}} when is_integer(N) -> [{max_queue, N}]; _ -> - case ejabberd_config:get_local_option( + case ejabberd_config:get_option( max_fsm_queue, fun(I) when is_integer(I), I > 0 -> I end) of undefined -> []; diff --git a/src/ejabberd_system_monitor.erl b/src/ejabberd_system_monitor.erl index 2f5d1c330..92a41b9d8 100644 --- a/src/ejabberd_system_monitor.erl +++ b/src/ejabberd_system_monitor.erl @@ -53,7 +53,7 @@ %% Description: Starts the server %%-------------------------------------------------------------------- start_link() -> - LH = ejabberd_config:get_local_option( + LH = ejabberd_config:get_option( watchdog_large_heap, fun(I) when is_integer(I), I > 0 -> I end, 1000000), @@ -200,7 +200,7 @@ send_message(From, To, Body) -> [{xmlcdata, Body}]}]}). get_admin_jids() -> - ejabberd_config:get_local_option( + ejabberd_config:get_option( watchdog_admins, fun(JIDs) -> [jlib:jid_tolower( diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 8a2bacf15..4506455bd 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -827,14 +827,14 @@ process_admin(Host, {value, {_, String}} -> case parse_access_rule(String) of {ok, Rs} -> - ejabberd_config:add_global_option({access, Name, Host}, + ejabberd_config:add_option({access, Name, Host}, Rs), ok; _ -> error end; _ -> nothing end, - Rules = case ejabberd_config:get_global_option( + Rules = case ejabberd_config:get_option( {access, Name, Host}, fun(V) -> V end) of undefined -> []; @@ -1198,7 +1198,7 @@ access_parse_addnew(_AccessRules, Host, Query) -> case lists:keysearch(<<"namenew">>, 1, Query) of {value, {_, String}} when String /= <<"">> -> Name = jlib:binary_to_atom(String), - ejabberd_config:add_global_option({access, Name, Host}, + ejabberd_config:add_option({access, Name, Host}, []), ok end. diff --git a/src/eldap_utils.erl b/src/eldap_utils.erl index 6209802a8..a18a04ed6 100644 --- a/src/eldap_utils.erl +++ b/src/eldap_utils.erl @@ -182,7 +182,7 @@ get_opt({Key, Host}, Opts, F) -> get_opt({Key, Host}, Opts, F, Default) -> case gen_mod:get_opt(Key, Opts, F, undefined) of undefined -> - ejabberd_config:get_local_option( + ejabberd_config:get_option( {Key, Host}, F, Default); Val -> Val diff --git a/src/extauth.erl b/src/extauth.erl index 23a409ce5..e57ac0b8d 100644 --- a/src/extauth.erl +++ b/src/extauth.erl @@ -106,7 +106,7 @@ random_instance(MaxNum) -> random:uniform(MaxNum) - 1. get_instances(Server) -> - ejabberd_config:get_local_option( + ejabberd_config:get_option( {extauth_instances, Server}, fun(V) when is_integer(V), V > 0 -> V diff --git a/src/gen_iq_handler.erl b/src/gen_iq_handler.erl index d53971fe4..628c1c90c 100644 --- a/src/gen_iq_handler.erl +++ b/src/gen_iq_handler.erl @@ -33,7 +33,7 @@ %% API -export([start_link/3, add_iq_handler/6, remove_iq_handler/3, stop_iq_handler/3, handle/7, - process_iq/6, check_type/1]). + process_iq/6, check_type/1, transform_module_options/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, @@ -46,7 +46,7 @@ -record(state, {host, module, function}). -type component() :: ejabberd_sm | ejabberd_local. --type type() :: no_queue | one_queue | {queues, pos_integer()} | parallel. +-type type() :: no_queue | one_queue | pos_integer() | parallel. -type opts() :: no_queue | {one_queue, pid()} | {queues, [pid()]} | parallel. %%==================================================================== @@ -71,7 +71,7 @@ add_iq_handler(Component, Host, NS, Module, Function, [Host, Module, Function]), Component:register_iq_handler(Host, NS, Module, Function, {one_queue, Pid}); - {queues, N} -> + N when is_integer(N) -> Pids = lists:map(fun (_) -> {ok, Pid} = supervisor:start_child(ejabberd_iq_sup, @@ -130,9 +130,19 @@ process_iq(_Host, Module, Function, From, To, IQ) -> check_type(no_queue) -> no_queue; check_type(one_queue) -> one_queue; -check_type({queues, N}) when is_integer(N), N>0 -> {queues, N}; +check_type(N) when is_integer(N), N>0 -> N; check_type(parallel) -> parallel. +-spec transform_module_options([{atom(), any()}]) -> [{atom(), any()}]. + +transform_module_options(Opts) -> + lists:map( + fun({iqdisc, {queues, N}}) -> + {iqdisc, N}; + (Opt) -> + Opt + end, Opts). + %%==================================================================== %% gen_server callbacks %%==================================================================== diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 5245dc65c..0b06529b5 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -64,13 +64,11 @@ start() -> -spec start_module(binary(), atom(), opts()) -> any(). start_module(Host, Module, Opts) -> - set_module_opts_mnesia(Host, Module, Opts), ets:insert(ejabberd_modules, #ejabberd_module{module_host = {Module, Host}, opts = Opts}), try Module:start(Host, Opts) catch Class:Reason -> - del_module_mnesia(Host, Module), ets:delete(ejabberd_modules, {Module, Host}), ErrorText = io_lib:format("Problem starting the module ~p for host " @@ -101,7 +99,7 @@ is_app_running(AppName) -> stop_module(Host, Module) -> case stop_module_keep_config(Host, Module) of error -> error; - ok -> del_module_mnesia(Host, Module) + ok -> ok end. %% @doc Stop the module in a host, but keep its configuration. @@ -232,25 +230,6 @@ loaded_modules_with_opts(Host) -> opts = '$2'}, [], [{{'$1', '$2'}}]}]). -set_module_opts_mnesia(Host, Module, Opts) -> - Modules = ejabberd_config:get_local_option( - {modules, Host}, - fun(Ls) when is_list(Ls) -> Ls end, - []), - Modules1 = lists:keydelete(Module, 1, Modules), - Modules2 = [{Module, Opts} | Modules1], - ejabberd_config:add_local_option({modules, Host}, - Modules2). - -del_module_mnesia(Host, Module) -> - Modules = ejabberd_config:get_local_option( - {modules, Host}, - fun(Ls) when is_list(Ls) -> Ls end, - []), - Modules1 = lists:keydelete(Module, 1, Modules), - ejabberd_config:add_local_option({modules, Host}, - Modules1). - -spec get_hosts(opts(), binary()) -> [binary()]. get_hosts(Opts, Prefix) -> diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index 9c2ab319b..087d090c8 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -3,7 +3,7 @@ %%% Author : Eric Cestari <ecestari@process-one.net> %%% Purpose : Message Carbons XEP-0280 0.8 %%% Created : 5 May 2008 by Mickael Remond <mremond@process-one.net> -%%% Usage : Add the following line in modules section of ejabberd.cfg: +%%% Usage : Add the following line in modules section of ejabberd.yml: %%% {mod_carboncopy, []} %%% %%% diff --git a/src/mod_configure2.erl b/src/mod_configure2.erl index e3fbe82dc..375621441 100644 --- a/src/mod_configure2.erl +++ b/src/mod_configure2.erl @@ -129,7 +129,7 @@ process_get(#xmlel{name = <<"info">>}) -> children = []}}; process_get(#xmlel{name = <<"welcome-message">>, attrs = Attrs}) -> - {Subj, Body} = ejabberd_config:get_local_option( + {Subj, Body} = ejabberd_config:get_option( welcome_message, fun({Subj, Body}) -> {iolist_to_binary(Subj), @@ -145,7 +145,7 @@ process_get(#xmlel{name = <<"welcome-message">>, children = [{xmlcdata, Body}]}]}}; process_get(#xmlel{name = <<"registration-watchers">>, attrs = Attrs}) -> - SubEls = ejabberd_config:get_local_option( + SubEls = ejabberd_config:get_option( registration_watchers, fun(JIDs) when is_list(JIDs) -> lists:map( diff --git a/src/mod_disco.erl b/src/mod_disco.erl index d62251686..df3f320c7 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -36,7 +36,8 @@ process_sm_iq_items/3, process_sm_iq_info/3, get_sm_identity/5, get_sm_features/5, get_sm_items/5, get_info/5, register_feature/2, unregister_feature/2, - register_extra_domain/2, unregister_extra_domain/2]). + register_extra_domain/2, unregister_extra_domain/2, + transform_module_options/1]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -440,6 +441,22 @@ get_user_resources(User, Server) -> end, lists:sort(Rs)). +transform_module_options(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). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Support for: XEP-0157 Contact Addresses for XMPP Services @@ -465,9 +482,17 @@ get_info(_A, Host, Mod, Node, _Lang) when Node == <<>> -> get_info(Acc, _, _, _Node, _) -> Acc. get_fields_xml(Host, Module) -> - Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info, - fun(L) when is_list(L) -> L end, - []), + Fields = gen_mod:get_module_opt( + Host, ?MODULE, server_info, + fun(L) -> + lists:map( + fun(Opts) -> + Mods = proplists:get_value(modules, Opts, all), + Name = proplists:get_value(names, Opts, <<>>), + URLs = proplists:get_value(urls, Opts, []), + {Mods, Name, URLs} + end, lists:flatmap(L)) + end, []), Fields_good = lists:filter(fun ({Modules, _, _}) -> case Modules of all -> true; diff --git a/src/mod_last.erl b/src/mod_last.erl index 3ea86b350..6e35938d2 100644 --- a/src/mod_last.erl +++ b/src/mod_last.erl @@ -32,7 +32,8 @@ -export([start/2, stop/1, process_local_iq/3, export/1, process_sm_iq/3, on_presence_update/4, import/1, import/3, - store_last_info/4, get_last_info/2, remove_user/2]). + store_last_info/4, get_last_info/2, remove_user/2, + transform_options/1]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -101,18 +102,13 @@ process_local_iq(_From, _To, %% @doc Get the uptime of the ejabberd node, expressed in seconds. %% When ejabberd is starting, ejabberd_config:start/0 stores the datetime. get_node_uptime() -> - case ejabberd_config:get_local_option( + case ejabberd_config:get_option( node_start, - fun({MegaSecs, Secs, MicroSecs} = Now) - when is_integer(MegaSecs), MegaSecs >= 0, - is_integer(Secs), Secs >= 0, - is_integer(MicroSecs), MicroSecs >= 0 -> - Now - end) of + fun(S) when is_integer(S), S >= 0 -> S end) of undefined -> trunc(element(1, erlang:statistics(wall_clock)) / 1000); - StartNow -> - now_to_seconds(now()) - now_to_seconds(StartNow) + Now -> + now_to_seconds(now()) - Now end. now_to_seconds({MegaSecs, Secs, _MicroSecs}) -> @@ -319,3 +315,13 @@ import(_LServer, mnesia, #last_activity{} = LA) -> mnesia:dirty_write(LA); import(_, _, _) -> pass. + +transform_options(Opts) -> + lists:foldl(fun transform_options/2, [], Opts). + +transform_options({node_start, {_, _, _} = Now}, Opts) -> + ?WARNING_MSG("Old 'node_start' format detected. This is still supported " + "but it is better to fix your config.", []), + [{node_start, now_to_seconds(Now)}|Opts]; +transform_options(Opt, Opts) -> + [Opt|Opts]. diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index 97d992145..357d19216 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -33,7 +33,7 @@ -behaviour(gen_mod). %% API --export([start_link/2, start/2, stop/1, +-export([start_link/2, start/2, stop/1, transform_module_options/1, check_access_log/2, add_to_log/5]). %% gen_server callbacks @@ -111,6 +111,14 @@ check_access_log(Host, From) -> Res -> Res end. +transform_module_options(Opts) -> + lists:map( + fun({top_link, {S1, S2}}) -> + {top_link, [{S1, S2}]}; + (Opt) -> + Opt + end, Opts). + %%==================================================================== %% gen_server callbacks %%==================================================================== @@ -152,14 +160,14 @@ init([Host, Opts]) -> (universal) -> universal end, local), Top_link = gen_mod:get_opt(top_link, Opts, - fun({S1, S2}) -> + fun([{S1, S2}]) -> {iolist_to_binary(S1), iolist_to_binary(S2)} end, {<<"/">>, <<"Home">>}), NoFollow = gen_mod:get_opt(spam_prevention, Opts, fun(B) when is_boolean(B) -> B end, true), - Lang = ejabberd_config:get_local_option( + Lang = ejabberd_config:get_option( {language, Host}, fun iolist_to_binary/1, ?MYLANG), diff --git a/src/mod_proxy65.erl b/src/mod_proxy65.erl index 3e8354caf..85bec283b 100644 --- a/src/mod_proxy65.erl +++ b/src/mod_proxy65.erl @@ -33,7 +33,7 @@ -behaviour(supervisor). %% gen_mod callbacks. --export([start/2, stop/1]). +-export([start/2, stop/1, transform_module_options/1]). %% supervisor callbacks. -export([init/1]). @@ -64,6 +64,9 @@ start_link(Host, Opts) -> supervisor:start_link({local, Proc}, ?MODULE, [Host, Opts]). +transform_module_options(Opts) -> + mod_proxy65_service:transform_module_options(Opts). + init([Host, Opts]) -> Service = {mod_proxy65_service, {mod_proxy65_service, start_link, [Host, Opts]}, diff --git a/src/mod_proxy65_service.erl b/src/mod_proxy65_service.erl index 7d16e0f17..33d40b1cc 100644 --- a/src/mod_proxy65_service.erl +++ b/src/mod_proxy65_service.erl @@ -35,7 +35,7 @@ handle_cast/2, terminate/2, code_change/3]). %% API. --export([start_link/2, add_listener/2, +-export([start_link/2, add_listener/2, transform_module_options/1, delete_listener/1]). -include("ejabberd.hrl"). @@ -261,16 +261,15 @@ parse_options(ServerHost, Opts) -> Name = gen_mod:get_opt(name, Opts, fun iolist_to_binary/1, <<"SOCKS5 Bytestreams">>), IP = gen_mod:get_opt(ip, Opts, - fun(Addr) -> - jlib:ip_to_list(Addr), + fun(S) -> + {ok, Addr} = inet_parse:address( + binary_to_list( + iolist_to_binary(S))), Addr end, get_my_ip()), HostName = gen_mod:get_opt(hostname, Opts, - fun(Addr) when is_tuple(Addr) -> - jlib:ip_to_list(Addr); - (S) -> - iolist_to_binary(S) - end, jlib:ip_to_list(IP)), + fun iolist_to_binary/1, + jlib:ip_to_list(IP)), StreamAddr = [{<<"jid">>, MyHost}, {<<"host">>, HostName}, {<<"port">>, jlib:integer_to_binary(Port)}], @@ -278,6 +277,16 @@ parse_options(ServerHost, Opts) -> name = Name, port = Port, ip = IP, stream_addr = StreamAddr, acl = ACL}. +transform_module_options(Opts) -> + lists:map( + fun({ip, IP}) when is_tuple(IP) -> + {ip, jlib:ip_to_list(IP)}; + ({hostname, IP}) when is_tuple(IP) -> + {hostname, jlib:ip_to_list(IP)}; + (Opt) -> + Opt + end, Opts). + get_my_ip() -> {ok, MyHostName} = inet:gethostname(), case inet:getaddr(MyHostName, inet) of diff --git a/src/mod_proxy65_stream.erl b/src/mod_proxy65_stream.erl index 9b861f4db..b37dcc13a 100644 --- a/src/mod_proxy65_stream.erl +++ b/src/mod_proxy65_stream.erl @@ -279,10 +279,14 @@ select_auth_method(anonymous, AuthMethods) -> %% Obviously, we must use shaper with maximum rate. find_maxrate(Shaper, JID1, JID2, Host) -> - MaxRate1 = shaper:new(acl:match_rule(Host, Shaper, - JID1)), - MaxRate2 = shaper:new(acl:match_rule(Host, Shaper, - JID2)), + MaxRate1 = case acl:match_rule(Host, Shaper, JID1) of + deny -> none; + R1 -> shaper:new(R1) + end, + MaxRate2 = case acl:match_rule(Host, Shaper, JID2) of + deny -> none; + R2 -> shaper:new(R2) + end, if MaxRate1 == none; MaxRate2 == none -> none; true -> lists:max([MaxRate1, MaxRate2]) end. diff --git a/src/mod_register.erl b/src/mod_register.erl index 2b4e53de6..197927df0 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -32,7 +32,8 @@ -export([start/2, stop/1, stream_feature_register/2, unauthenticated_iq_register/4, try_register/5, - process_iq/3, send_registration_notifications/3]). + process_iq/3, send_registration_notifications/3, + transform_options/1, transform_module_options/1]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -418,7 +419,11 @@ try_register(User, Server, Password, SourceRaw, Lang) -> send_welcome_message(JID) -> Host = JID#jid.lserver, case gen_mod:get_module_opt(Host, ?MODULE, welcome_message, - fun({S, B}) -> + fun(Opts) -> + S = proplists:get_value( + subject, Opts, <<>>), + B = proplists:get_value( + body, Opts, <<>>), {iolist_to_binary(S), iolist_to_binary(B)} end, {<<"">>, <<"">>}) @@ -483,7 +488,7 @@ check_from(JID, Server) -> check_timeout(undefined) -> true; check_timeout(Source) -> - Timeout = ejabberd_config:get_local_option( + Timeout = ejabberd_config:get_option( registration_timeout, fun(TO) when is_integer(TO), TO > 0 -> TO; @@ -537,7 +542,7 @@ clean_treap(Treap, CleanPriority) -> remove_timeout(undefined) -> true; remove_timeout(Source) -> - Timeout = ejabberd_config:get_local_option( + Timeout = ejabberd_config:get_option( registration_timeout, fun(TO) when is_integer(TO), TO > 0 -> TO; @@ -604,6 +609,54 @@ is_strong_password(Server, Password) -> ejabberd_auth:entropy(Password) >= Entropy end. +transform_options(Opts) -> + Opts1 = transform_ip_access(Opts), + transform_module_options(Opts1). + +transform_ip_access(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 = jlib: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. + +transform_module_options(Opts) -> + lists:flatmap( + fun({welcome_message, {Subj, Body}}) -> + ?WARNING_MSG("Old 'welcome_message' format detected. " + "The old format is still supported " + "but it is better to fix your config: " + "change it to {welcome_message, " + "[{subject, Subject}, {body, Body}]}", + []), + [{welcome_message, [{subject, Subj}, {body, Body}]}]; + (Opt) -> + [Opt] + end, Opts). + %%% %%% ip_access management %%% @@ -614,75 +667,15 @@ may_remove_resource(From) -> From. get_ip_access(Host) -> gen_mod:get_module_opt(Host, ?MODULE, ip_access, - fun(IPAccess) -> - lists:flatmap( - fun({Access, S}) -> - {ok, IP, Mask} = - parse_ip_netmask( - iolist_to_binary(S)), - [{Access, IP, Mask}] - end, IPAccess) - end, []). - -parse_ip_netmask(S) -> - case str:tokens(S, <<"/">>) of - [IPStr] -> - case inet_parse:address(binary_to_list(IPStr)) of - {ok, {_, _, _, _} = IP} -> {ok, IP, 32}; - {ok, {_, _, _, _, _, _, _, _} = IP} -> {ok, IP, 128}; - _ -> error - end; - [IPStr, MaskStr] -> - case catch jlib:binary_to_integer(MaskStr) of - Mask when is_integer(Mask), Mask >= 0 -> - case inet_parse:address(binary_to_list(IPStr)) of - {ok, {_, _, _, _} = IP} when Mask =< 32 -> - {ok, IP, Mask}; - {ok, {_, _, _, _, _, _, _, _} = IP} when Mask =< 128 -> - {ok, IP, Mask}; - _ -> error - end; - _ -> error - end; - _ -> error - end. + fun(A) when is_atom(A) -> A end, + all). -check_ip_access(_Source, []) -> allow; check_ip_access({User, Server, Resource}, IPAccess) -> case ejabberd_sm:get_user_ip(User, Server, Resource) of - {IPAddress, _PortNumber} -> - check_ip_access(IPAddress, IPAccess); - _ -> true - end; -check_ip_access({_, _, _, _} = IP, - [{Access, {_, _, _, _} = Net, Mask} | IPAccess]) -> - IPInt = ip_to_integer(IP), - NetInt = ip_to_integer(Net), - M = bnot (1 bsl (32 - Mask) - 1), - if IPInt band M =:= NetInt band M -> Access; - true -> check_ip_access(IP, IPAccess) - end; -check_ip_access({_, _, _, _, _, _, _, _} = IP, - [{Access, {_, _, _, _, _, _, _, _} = Net, Mask} - | IPAccess]) -> - IPInt = ip_to_integer(IP), - NetInt = ip_to_integer(Net), - M = bnot (1 bsl (128 - Mask) - 1), - if IPInt band M =:= NetInt band M -> Access; - true -> check_ip_access(IP, IPAccess) + {IPAddress, _PortNumber} -> + check_ip_access(IPAddress, IPAccess); + _ -> + deny end; -check_ip_access(IP, [_ | IPAccess]) -> - check_ip_access(IP, IPAccess). - -ip_to_integer({IP1, IP2, IP3, IP4}) -> - IP1 bsl 8 bor IP2 bsl 8 bor IP3 bsl 8 bor IP4; -ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7, - IP8}) -> - IP1 bsl 16 bor IP2 bsl 16 bor IP3 bsl 16 bor IP4 bsl 16 - bor IP5 - bsl 16 - bor IP6 - bsl 16 - bor IP7 - bsl 16 - bor IP8. +check_ip_access(IPAddress, IPAccess) -> + acl:match_rule(global, IPAccess, IPAddress). diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl index 7711353d2..1c96dd4a1 100644 --- a/src/mod_vcard_ldap.erl +++ b/src/mod_vcard_ldap.erl @@ -38,7 +38,7 @@ -export([start/2, start_link/2, stop/1, get_sm_features/5, process_local_iq/3, process_sm_iq/3, - remove_user/1, route/4]). + remove_user/1, route/4, transform_module_options/1]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -767,7 +767,7 @@ parse_options(Host, Opts) -> VCardMap = gen_mod:get_opt(ldap_vcard_map, Opts, fun(Ls) -> lists:map( - fun({S, P, L}) -> + fun({S, [{P, L}]}) -> {iolist_to_binary(S), iolist_to_binary(P), [iolist_to_binary(E) @@ -823,6 +823,20 @@ parse_options(Host, Opts) -> search_reported_attrs = SearchReportedAttrs, matches = Matches}. +transform_module_options(Opts) -> + lists:map( + fun({ldap_vcard_map, Map}) -> + NewMap = lists:map( + fun({Field, Pattern, Attrs}) -> + {Field, [{Pattern, Attrs}]}; + (Opt) -> + Opt + end, Map), + {ldap_vcard_map, NewMap}; + (Opt) -> + Opt + end, Opts). + check_filter(F) -> NewF = iolist_to_binary(F), {ok, _} = eldap_filter:parse(NewF), diff --git a/src/node_pep.erl b/src/node_pep.erl index f4b0bb38c..5635dee94 100644 --- a/src/node_pep.erl +++ b/src/node_pep.erl @@ -490,13 +490,11 @@ path_to_node(Path) -> node_flat:path_to_node(Path). %% Check that the mod_caps module is enabled in that Jabber Host %% If not, show a warning message in the ejabberd log file. complain_if_modcaps_disabled(ServerHost) -> - Modules = ejabberd_config:get_local_option({modules, ServerHost}, fun(Ms) when is_list(Ms) -> Ms end), - ModCaps = [mod_caps_enabled || {mod_caps, _Opts} <- Modules], - case ModCaps of - [] -> + case gen_mod:is_loaded(ServerHost, mod_caps) of + false -> ?WARNING_MSG("The PEP plugin is enabled in mod_pubsub " "of host ~p. This plugin requires mod_caps " "to be enabled, but it isn't.", [ServerHost]); - _ -> ok + true -> ok end. diff --git a/src/node_pep_odbc.erl b/src/node_pep_odbc.erl index 81cb7bc53..e4e1fafa9 100644 --- a/src/node_pep_odbc.erl +++ b/src/node_pep_odbc.erl @@ -433,7 +433,7 @@ path_to_node(Path) -> node_flat_odbc:path_to_node(Path). %% Check that the mod_caps module is enabled in that Jabber Host %% If not, show a warning message in the ejabberd log file. complain_if_modcaps_disabled(ServerHost) -> - Modules = ejabberd_config:get_local_option({modules, + Modules = ejabberd_config:get_option({modules, ServerHost}, fun(Ms) when is_list(Ms) -> Ms end), ModCaps = [mod_caps_enabled diff --git a/src/odbc_queries.erl b/src/odbc_queries.erl index 23b8e8da6..314b7cc13 100644 --- a/src/odbc_queries.erl +++ b/src/odbc_queries.erl @@ -219,13 +219,15 @@ list_users(LServer, [Prefix, Limit, Offset]))]). users_number(LServer) -> - case element(1, - ejabberd_config:get_local_option( - {odbc_server, LServer}, fun(V) -> V end)) - of + Type = ejabberd_config:get_option({odbc_type, LServer}, + fun(pgsql) -> pgsql; + (mysql) -> mysql; + (odbc) -> odbc + end, odbc), + case Type of pgsql -> case - ejabberd_config:get_local_option( + ejabberd_config:get_option( {pgsql_users_number_estimate, LServer}, fun(V) when is_boolean(V) -> V end, false) diff --git a/src/shaper.erl b/src/shaper.erl index 37c1a30c9..41e5f0626 100644 --- a/src/shaper.erl +++ b/src/shaper.erl @@ -28,7 +28,8 @@ -author('alexey@process-one.net'). --export([new/1, new1/1, update/2]). +-export([start/0, new/1, new1/1, update/2, + transform_options/1, load_from_config/0]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -37,32 +38,66 @@ lastrate = 0.0 :: float(), lasttime = 0 :: integer()}). --type maxrate() :: none | #maxrate{}. +-record(shaper, {name :: {atom(), global}, + maxrate :: integer()}). --type shaper() :: maxrate() | {maxrate(), integer()}. +-type shaper() :: none | #maxrate{}. -export_type([shaper/0]). --spec new(atom()) -> maxrate(). +-spec start() -> ok. +start() -> + mnesia:create_table(shaper, + [{ram_copies, [node()]}, + {local_content, true}, + {attributes, record_info(fields, shaper)}]), + mnesia:add_table_copy(shaper, node(), ram_copies), + load_from_config(), + ok. + +-spec load_from_config() -> ok | {error, any()}. + +load_from_config() -> + Shapers = ejabberd_config:get_option( + shaper, fun(V) -> V end, []), + case mnesia:transaction( + fun() -> + lists:foreach( + fun({Name, MaxRate}) -> + mnesia:write(#shaper{name = {Name, global}, + maxrate = MaxRate}) + end, Shapers) + end) of + {atomic, ok} -> + ok; + Err -> + {error, Err} + end. + +-spec new(atom()) -> shaper(). + +new(none) -> + none; new(Name) -> - Data = ejabberd_config:get_global_option( - {shaper, Name, global}, - fun({maxrate, R}) when is_integer(R), R>0 -> - {maxrate, R}; - (none) -> - none - end, none), - new1(Data). + MaxRate = case ets:lookup(shaper, {Name, global}) of + [#shaper{maxrate = R}] -> + R; + [] -> + ?WARNING_MSG("Attempt to initialize an " + "unspecified shaper '~s'", [Name]), + none + end, + new1(MaxRate). --spec new1(none | {maxrate, integer()}) -> maxrate(). +-spec new1(none | integer()) -> shaper(). new1(none) -> none; -new1({maxrate, MaxRate}) -> +new1(MaxRate) -> #maxrate{maxrate = MaxRate, lastrate = 0.0, lasttime = now_to_usec(now())}. --spec update(maxrate(), integer()) -> {maxrate(), integer()}. +-spec update(shaper(), integer()) -> {shaper(), integer()}. update(none, _Size) -> {none, 0}; update(#maxrate{} = State, Size) -> @@ -84,5 +119,15 @@ update(#maxrate{} = State, Size) -> lasttime = NextNow}, Pause}. +transform_options(Opts) -> + lists:foldl(fun transform_options/2, [], Opts). + +transform_options({shaper, Name, {maxrate, N}}, Opts) -> + [{shaper, [{Name, N}]}|Opts]; +transform_options({shaper, Name, none}, Opts) -> + [{shaper, [{Name, none}]}|Opts]; +transform_options(Opt, Opts) -> + [Opt|Opts]. + now_to_usec({MSec, Sec, USec}) -> (MSec * 1000000 + Sec) * 1000000 + USec. |