%% @author Marc Worrell <marc@worrell.nl>
%% @copyright 2010-2021 Marc Worrell, 2014 Arjan Scherpenisse
%% @doc Wrapper for Zotonic application environment configuration
%% Copyright 2010-2021 Marc Worrell, 2014 Arjan Scherpenisse
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-module(z_config).
-author("Marc Worrell <marc@worrell.nl>").
%% API export
-export([
get/1,
get/2,
init_app_env/0,
maybe_map_env/1,
all/0
]).
-include_lib("zotonic.hrl").
%%====================================================================
%% API
%%====================================================================
%% @doc Copy some zotonic config settings over to other applications
-spec init_app_env() -> ok.
init_app_env() ->
application:set_env(cowmachine, proxy_allowlist, ?MODULE:get(proxy_allowlist)),
application:set_env(cowmachine, ip_allowlist, ?MODULE:get(ip_allowlist)),
ok.
%% @doc Get value from config file (cached)
%%
%% Some config settings can be overruled by environment settings.
-spec get(atom()) -> any().
get(listen_ip) ->
IPv4 = case os:getenv("ZOTONIC_IP") of
false -> ?MODULE:get(listen_ip, default(listen_ip));
IP -> IP
end,
maybe_map_value(listen_ip, IPv4);
get(listen_ip6) ->
IPv6 = case os:getenv("ZOTONIC_IP6") of
false -> get(listen_ip6, default(listen_ip6));
IP -> IP
end,
maybe_map_value(listen_ip6, IPv6);
get(listen_port) ->
case os:getenv("ZOTONIC_LISTEN_PORT") of
false -> ?MODULE:get(listen_port, default(listen_port));
"" -> ?MODULE:get(listen_port, default(listen_port));
"none" -> none;
Port -> list_to_integer(Port)
end;
get(port) ->
case os:getenv("ZOTONIC_PORT") of
false -> get(port, default(port));
"" -> get(port, default(port));
"none" -> none;
Port -> list_to_integer(Port)
end;
get(ssl_listen_port) ->
case os:getenv("ZOTONIC_SSL_LISTEN_PORT") of
false -> ?MODULE:get(ssl_listen_port, default(ssl_listen_port));
"" -> ?MODULE:get(ssl_listen_port, default(ssl_listen_port));
"none" -> none;
Port -> list_to_integer(Port)
end;
get(ssl_port) ->
case os:getenv("ZOTONIC_SSL_PORT") of
false -> get(ssl_port, default(ssl_port));
"" -> get(ssl_port, default(ssl_port));
% "none" -> none;
Port -> list_to_integer(Port)
end;
get(smtp_listen_domain) ->
case os:getenv("ZOTONIC_SMTP_LISTEN_DOMAIN") of
false -> get(smtp_listen_domain, default(smtp_listen_domain));
SmtpListenDomain_ -> SmtpListenDomain_
end;
get(smtp_listen_ip) ->
SmtpIp = case os:getenv("ZOTONIC_SMTP_LISTEN_IP") of
false -> ?MODULE:get(smtp_listen_ip, default(smtp_listen_ip));
"none" -> none;
SmtpListenIp -> SmtpListenIp
end,
maybe_map_value(smtp_listen_ip, SmtpIp);
get(smtp_listen_port) ->
case os:getenv("ZOTONIC_SMTP_LISTEN_PORT") of
false -> ?MODULE:get(smtp_listen_port, default(smtp_listen_port));
"none" -> none;
SmtpListenPort_ -> list_to_integer(SmtpListenPort_)
end;
get(smtp_spamd_ip) ->
maybe_map_value(smtp_spamd_ip, ?MODULE:get(smtp_spamd_ip, default(smtp_spamd_ip)));
get(zotonic_apps) ->
case os:getenv("ZOTONIC_APPS") of
false -> default(zotonic_apps);
"" -> default(zotonic_apps);
ZC -> ZC
end;
get(dbhost) ->
case os:getenv("ZOTONIC_DBHOST") of
false -> default(dbhost);
"" -> default(dbhost);
DBHost -> DBHost
end;
get(dbport) ->
case os:getenv("ZOTONIC_DBPORT") of
false -> default(dbport);
"" -> default(dbport);
DBPort -> list_to_integer(DBPort)
end;
get(Key) ->
?MODULE:get(Key, default(Key)).
%% @doc Get value from config file, returning default value when not set (cached).
-spec get(atom(), any()) -> any().
get(Key, Default) ->
case application:get_env(zotonic, Key) of
undefined ->
maybe_map_value(Key, maybe_map_env(Default));
{ok, Value} ->
maybe_map_value(Key, maybe_map_env(Value))
end.
maybe_map_env({env, Name}) -> os:getenv(Name);
maybe_map_env({env, Name, Default}) -> os:getenv(Name, Default);
maybe_map_env({env_int, Name}) -> z_convert:to_integer(os:getenv(Name));
maybe_map_env({env_int, Name, Default}) -> z_convert:to_integer(os:getenv(Name, Default));
maybe_map_env(V) -> V.
%% @doc Translate IP addresses to a tuple(), 'any', or 'none'
-spec maybe_map_value(atom(), term()) -> term().
maybe_map_value(listen_ip, IP) -> map_ip_address(listen_ip, IP);
maybe_map_value(listen_ip6, IP) -> map_ip_address(listen_ip6, IP);
maybe_map_value(mqtt_listen_ip, IP) -> map_ip_address(mqtt_listen_ip, IP);
maybe_map_value(mqtt_listen_ip6, IP) -> map_ip_address(mqtt_listen_ip6, IP);
maybe_map_value(smtp_listen_ip, IP) -> map_ip_address(smtp_listen_ip, IP);
maybe_map_value(smtp_spamd_ip, IP) -> map_ip_address(smtp_spamd_ip, IP);
maybe_map_value(_Key, Value) ->
Value.
map_ip_address(_Name, any) -> any;
map_ip_address(_Name, "any") -> any;
map_ip_address(_Name, "") -> any;
map_ip_address(_Name, "*") -> any;
map_ip_address(_Name, <<>>) -> any;
map_ip_address(_Name, <<"any">>) -> any;
map_ip_address(_Name, <<"*">>) -> any;
map_ip_address(_Name, none) -> none;
map_ip_address(_Name, "none") -> none;
map_ip_address(_Name, <<"none">>) -> none;
map_ip_address(_Name, IP) when is_tuple(IP) ->
IP;
map_ip_address(Name, IP) when is_list(IP) ->
case getaddr(Name, IP) of
{ok, IpN} -> IpN;
{error, Reason} ->
?LOG_ERROR(#{
text => <<"Invalid IP address for config, assuming 'none">>,
in => zotonic_core,
config_name => Name,
ip => IP,
result => error,
reason => Reason
}),
none
end;
map_ip_address(Name, IP) when is_binary(IP) ->
map_ip_address(Name, binary_to_list(IP));
map_ip_address(smtp_spamd_ip, undefined) ->
none;
map_ip_address(Name, IP) ->
?LOG_ERROR(#{
text => <<"Invalid IP address for config, assuming 'any'">>,
in => zotonic_core,
config_name => Name,
ip => IP,
result => error,
reason => invalid_ip
}),
any.
getaddr(listen_ip6, IP) -> inet:getaddr(IP, inet6);
getaddr(_Name, IP) -> inet:getaddr(IP, inet).
default(environment) -> production; % development | test | acceptance | production | education | backup
default(security_dir) ->
case z_config_files:security_dir() of
{ok, Dir} -> Dir;
{error, _} -> undefined
end;
default(data_dir) ->
case z_config_files:data_dir() of
{ok, Dir} -> Dir;
{error, _} -> undefined
end;
default(log_dir) ->
case z_config_files:log_dir() of
{ok, Dir} -> Dir;
{error, _} -> undefined
end;
default(cache_dir) ->
case z_config_files:cache_dir() of
{ok, Dir} -> Dir;
{error, _} -> undefined
end;
default(timezone) -> <<"UTC">>;
default(listen_ip) -> any;
default(listen_ip6) ->
% Default to the listen_ip configuration iff that configuration
% is not a IPv4 address
ListenIP4 = case os:getenv("ZOTONIC_IP") of
false -> ?MODULE:get(listen_ip, default(listen_ip));
IP -> IP
end,
case ListenIP4 of
any -> any;
"any" -> any;
"" -> any;
"*" -> any;
none -> none;
"none" -> none;
{127,0,0,1} -> "::1";
{_,_,_,_} -> none;
Domain when is_list(Domain) ->
% Only use the domain if it is not a dotted ip4 number
case re:run(Domain, "^[0-9]{1,3}(\\.[0-9]{1,3}){3}$") of
{match, _} -> none;
_ -> Domain
end
end;
default(listen_port) -> 8000;
default(ssl_listen_port) -> 8443;
default(port) ->
case ?MODULE:get(listen_port) of
none -> 80;
ListenPort -> ListenPort
end;
default(ssl_port) ->
case ?MODULE:get(ssl_listen_port) of
none -> 443;
ListenPort -> ListenPort
end;
default(max_connections) -> 20000;
default(ssl_max_connections) -> 20000;
default(log_http_buffer_size) -> 10000;
default(security_headers) -> true;
default(smtp_verp_as_from) -> false;
default(smtp_no_mx_lookups) -> false;
default(smtp_relay) -> false;
default(smtp_host) -> "localhost";
default(smtp_port) -> 25;
default(smtp_ssl) -> false;
default(smtp_listen_ip) -> {127,0,0,1};
default(smtp_listen_port) -> 2525;
default(smtp_spamd_ip) -> none;
default(smtp_spamd_port) -> 783;
default(smtp_dns_blocklist) -> z_email_dnsbl:dns_blocklist();
default(smtp_dns_allowlist) -> z_email_dnsbl:dns_allowlist();
default(smtp_delete_sent_after) -> 240;
default(smtp_is_blackhole) -> false;
default(mqtt_listen_ip) -> ?MODULE:get(listen_ip);
default(mqtt_listen_ip6) -> ?MODULE:get(listen_ip6);
default(mqtt_listen_port) -> 1883;
default(mqtt_listen_ssl_port) -> 8883;
default(mqtt_max_connections) -> 10000;
default(mqtt_ssl_max_connections) -> 10000;
default(inet_backlog) -> 500;
default(inet_acceptor_pool_size) -> 100;
default(ssl_backlog) -> ?MODULE:get(inet_backlog);
default(ssl_acceptor_pool_size) -> ?MODULE:get(inet_acceptor_pool_size);
default(ssl_dhfile) -> undefined;
default(dbhost) -> "localhost";
default(dbport) -> 5432;
default(dbuser) -> "zotonic";
default(dbpassword) -> "zotonic";
default(dbdatabase) -> "zotonic";
default(dbschema) -> "public";
default(filewatcher_enabled) -> true;
default(filewatcher_scanner_enabled) -> true;
default(filewatcher_scanner_interval) -> 10000;
default(filewatcher_terminal_notifier) -> true;
default(syslog_ident) -> "zotonic";
default(syslog_opts) -> [ndelay];
default(syslog_facility) -> local0;
default(syslog_level) -> info;
default(log_http_metrics_buffer_size) -> 10000;
default(zotonic_apps) -> filename:join([ z_path:get_path(), "apps_user" ]);
default(proxy_allowlist) -> local;
default(ip_allowlist) -> local;
default(ip_allowlist_system_management) -> any;
default(sessionjobs_limit) -> erlang:max(erlang:system_info(process_limit) div 10, 10000);
default(sidejobs_limit) -> erlang:max(erlang:system_info(process_limit) div 2, 50000);
default(server_header) -> "Zotonic";
default(html_error_path) -> filename:join(code:priv_dir(zotonic_core), "htmlerrors");
default(_) -> undefined.
-spec all() -> list( {atom(), term()}) .
all() ->
lists:map(
fun
(ssl_dhfile) ->
{ssl_dhfile, z_ssl_dhfile:dhfile()};
(security_dir) ->
case z_config_files:security_dir() of
{ok, SecurityDir} ->
{security_dir, SecurityDir};
{error, Reason} ->
{security_dir, <<"ERROR: ", (z_convert:to_binary(Reason))/binary>>}
end;
(K) ->
{K, ?MODULE:get(K)}
end,
[
environment,
zotonic_apps,
security_dir,
data_dir,
log_dir,
cache_dir,
password,
timezone,
listen_ip,
listen_ip6,
listen_port,
ssl_listen_port,
port,
ssl_port,
max_connections,
ssl_max_connections,
dbhost,
dbport,
dbuser,
dbpassword,
dbdatabase,
dbschema,
security_headers,
smtp_verp_as_from,
smtp_no_mx_lookups,
smtp_relay,
smtp_username,
smtp_password,
smtp_host,
smtp_port,
smtp_ssl,
smtp_listen_ip,
smtp_listen_port,
smtp_spamd_ip,
smtp_spamd_port,
smtp_dns_blocklist,
smtp_dns_allowlist,
smtp_delete_sent_after,
mqtt_listen_ip,
mqtt_listen_ip6,
mqtt_listen_port,
mqtt_listen_ssl_port,
mqtt_max_connections,
mqtt_ssl_max_connections,
inet_backlog,
inet_acceptor_pool_size,
ssl_backlog,
ssl_acceptor_pool_size,
ssl_dhfile,
filewatcher_enabled,
filewatcher_scanner_enabled,
filewatcher_scanner_interval,
syslog_ident,
syslog_opts,
syslog_facility,
syslog_level,
proxy_allowlist,
ip_allowlist,
ip_allowlist_system_management,
sessionjobs_limit,
sidejobs_limit,
log_http_metrics_buffer_size,
server_header,
html_error_path
]).