util.x509: Only collect commonNames that pass idna
[prosody.git] / core / configmanager.lua
blob090a6a0aa2c11650cf85e508444d9cbe94c916e5
1 -- Prosody IM
2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
4 --
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
7 --
9 local _G = _G;
10 local setmetatable, rawget, rawset, io, os, error, dofile, type, pairs, ipairs =
11 setmetatable, rawget, rawset, io, os, error, dofile, type, pairs, ipairs;
12 local format, math_max, t_insert = string.format, math.max, table.insert;
14 local envload = require"util.envload".envload;
15 local deps = require"util.dependencies";
16 local resolve_relative_path = require"util.paths".resolve_relative_path;
17 local glob_to_pattern = require"util.paths".glob_to_pattern;
18 local path_sep = package.config:sub(1,1);
19 local get_traceback_table = require "util.debug".get_traceback_table;
21 local encodings = deps.softreq"util.encodings";
22 local nameprep = encodings and encodings.stringprep.nameprep or function (host) return host:lower(); end
24 local _M = {};
25 local _ENV = nil;
26 -- luacheck: std none
28 _M.resolve_relative_path = resolve_relative_path; -- COMPAT
30 local parser = nil;
32 local config_mt = { __index = function (t, _) return rawget(t, "*"); end};
33 local config = setmetatable({ ["*"] = { } }, config_mt);
35 -- When host not found, use global
36 local host_mt = { __index = function(_, k) return config["*"][k] end }
38 function _M.getconfig()
39 return config;
40 end
42 function _M.get(host, key, _oldkey)
43 if key == "core" then
44 key = _oldkey; -- COMPAT with code that still uses "core"
45 end
46 return config[host][key];
47 end
48 function _M.rawget(host, key, _oldkey)
49 if key == "core" then
50 key = _oldkey; -- COMPAT with code that still uses "core"
51 end
52 local hostconfig = rawget(config, host);
53 if hostconfig then
54 return rawget(hostconfig, key);
55 end
56 end
58 local function set(config_table, host, key, value)
59 if host and key then
60 local hostconfig = rawget(config_table, host);
61 if not hostconfig then
62 hostconfig = rawset(config_table, host, setmetatable({}, host_mt))[host];
63 end
64 hostconfig[key] = value;
65 return true;
66 end
67 return false;
68 end
70 function _M.set(host, key, value, _oldvalue)
71 if key == "core" then
72 key, value = value, _oldvalue; --COMPAT with code that still uses "core"
73 end
74 return set(config, host, key, value);
75 end
77 function _M.load(filename, config_format)
78 config_format = config_format or filename:match("%w+$");
80 if config_format == "lua" then
81 local f, err = io.open(filename);
82 if f then
83 local new_config = setmetatable({ ["*"] = { } }, config_mt);
84 local ok, err = parser.load(f:read("*a"), filename, new_config);
85 f:close();
86 if ok then
87 config = new_config;
88 end
89 return ok, "parser", err;
90 end
91 return f, "file", err;
92 end
94 if not config_format then
95 return nil, "file", "no parser specified";
96 else
97 return nil, "file", "no parser for "..(config_format);
98 end
99 end
101 -- Built-in Lua parser
103 local pcall = _G.pcall;
104 local function get_line_number(config_file)
105 local tb = get_traceback_table(nil, 2);
106 for i = 1, #tb do
107 if tb[i].info.short_src == config_file then
108 return tb[i].info.currentline;
112 parser = {};
113 function parser.load(data, config_file, config_table)
114 local set_options = {}; -- set_options[host.."/"..option_name] = true (when the option has been set already in this file)
115 local warnings = {};
116 local env;
117 -- The ' = true' are needed so as not to set off __newindex when we assign the functions below
118 env = setmetatable({
119 Host = true, host = true, VirtualHost = true,
120 Component = true, component = true,
121 Include = true, include = true, RunScript = true }, {
122 __index = function (_, k)
123 if k:match("^ENV_") then
124 return os.getenv(k:sub(5));
126 return rawget(_G, k);
127 end,
128 __newindex = function (_, k, v)
129 local host = env.__currenthost or "*";
130 local option_path = host.."/"..k;
131 if set_options[option_path] then
132 t_insert(warnings, ("%s:%d: Duplicate option '%s'"):format(config_file, get_line_number(config_file), k));
134 set_options[option_path] = true;
135 set(config_table, env.__currenthost or "*", k, v);
139 rawset(env, "__currenthost", "*") -- Default is global
140 function env.VirtualHost(name)
141 name = nameprep(name);
142 if rawget(config_table, name) and rawget(config_table[name], "component_module") then
143 error(format("Host %q clashes with previously defined %s Component %q, for services use a sub-domain like conference.%s",
144 name, config_table[name].component_module:gsub("^%a+$", { component = "external", muc = "MUC"}), name, name), 0);
146 rawset(env, "__currenthost", name);
147 -- Needs at least one setting to logically exist :)
148 set(config_table, name or "*", "defined", true);
149 return function (config_options)
150 rawset(env, "__currenthost", "*"); -- Return to global scope
151 for option_name, option_value in pairs(config_options) do
152 set(config_table, name or "*", option_name, option_value);
154 end;
156 env.Host, env.host = env.VirtualHost, env.VirtualHost;
158 function env.Component(name)
159 name = nameprep(name);
160 if rawget(config_table, name) and rawget(config_table[name], "defined")
161 and not rawget(config_table[name], "component_module") then
162 error(format("Component %q clashes with previously defined Host %q, for services use a sub-domain like conference.%s",
163 name, name, name), 0);
165 set(config_table, name, "component_module", "component");
166 -- Don't load the global modules by default
167 set(config_table, name, "load_global_modules", false);
168 rawset(env, "__currenthost", name);
169 local function handle_config_options(config_options)
170 rawset(env, "__currenthost", "*"); -- Return to global scope
171 for option_name, option_value in pairs(config_options) do
172 set(config_table, name or "*", option_name, option_value);
176 return function (module)
177 if type(module) == "string" then
178 set(config_table, name, "component_module", module);
179 return handle_config_options;
181 return handle_config_options(module);
184 env.component = env.Component;
186 function env.Include(file)
187 -- Check whether this is a wildcard Include
188 if file:match("[*?]") then
189 local lfs = deps.softreq "lfs";
190 if not lfs then
191 error(format("Error expanding wildcard pattern in Include %q - LuaFileSystem not available", file));
193 local path_pos, glob = file:match("()([^"..path_sep.."]+)$");
194 local path = file:sub(1, math_max(path_pos-2,0));
195 local config_path = config_file:gsub("[^"..path_sep.."]+$", "");
196 if #path > 0 then
197 path = resolve_relative_path(config_path, path);
198 else
199 path = config_path;
201 local patt = glob_to_pattern(glob);
202 for f in lfs.dir(path) do
203 if f:sub(1,1) ~= "." and f:match(patt) then
204 env.Include(path..path_sep..f);
207 return;
209 -- Not a wildcard, so resolve (potentially) relative path and run through config parser
210 file = resolve_relative_path(config_file:gsub("[^"..path_sep.."]+$", ""), file);
211 local f, err = io.open(file);
212 if f then
213 local ret, err = parser.load(f:read("*a"), file, config_table);
214 if not ret then error(err:gsub("%[string.-%]", file), 0); end
215 if err then
216 for _, warning in ipairs(err) do
217 t_insert(warnings, warning);
221 if not f then error("Error loading included "..file..": "..err, 0); end
222 return f, err;
224 env.include = env.Include;
226 function env.RunScript(file)
227 return dofile(resolve_relative_path(config_file:gsub("[^"..path_sep.."]+$", ""), file));
230 local chunk, err = envload(data, "@"..config_file, env);
232 if not chunk then
233 return nil, err;
236 local ok, err = pcall(chunk);
238 if not ok then
239 return nil, err;
242 return true, warnings;
247 return _M;