xmlhandlers: Don't restrict CDATA
[prosody.git] / core / configmanager.lua
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 --
8
9
10
11 local _G = _G;
12 local   setmetatable, loadfile, pcall, rawget, rawset, io, error, dofile, type, pairs, table, format =
13                 setmetatable, loadfile, pcall, rawget, rawset, io, error, dofile, type, pairs, table, string.format;
14
15
16 local eventmanager = require "core.eventmanager";
17
18 module "configmanager"
19
20 local parsers = {};
21
22 local config = { ["*"] = { core = {} } };
23
24 local global_config = config["*"];
25
26 -- When host not found, use global
27 setmetatable(config, { __index = function () return global_config; end});
28 local host_mt = { __index = global_config };
29
30 -- When key not found in section, check key in global's section
31 function section_mt(section_name)
32         return { __index =      function (t, k)
33                                         local section = rawget(global_config, section_name);
34                                         if not section then return nil; end
35                                         return section[k];
36                                 end
37         };
38 end
39
40 function getconfig()
41         return config;
42 end
43
44 function get(host, section, key)
45         local sec = config[host][section];
46         if sec then
47                 return sec[key];
48         end
49         return nil;
50 end
51
52 function set(host, section, key, value)
53         if host and section and key then
54                 local hostconfig = rawget(config, host);
55                 if not hostconfig then
56                         hostconfig = rawset(config, host, setmetatable({}, host_mt))[host];
57                 end
58                 if not rawget(hostconfig, section) then
59                         hostconfig[section] = setmetatable({}, section_mt(section));
60                 end
61                 hostconfig[section][key] = value;
62                 return true;
63         end
64         return false;
65 end
66
67 function load(filename, format)
68         format = format or filename:match("%w+$");
69
70         if parsers[format] and parsers[format].load then
71                 local f, err = io.open(filename);
72                 if f then
73                         local ok, err = parsers[format].load(f:read("*a"), filename);
74                         f:close();
75                         if ok then
76                                 eventmanager.fire_event("config-reloaded", { filename = filename, format = format });
77                         end
78                         return ok, "parser", err;
79                 end
80                 return f, "file", err;
81         end
82
83         if not format then
84                 return nil, "file", "no parser specified";
85         else
86                 return nil, "file", "no parser for "..(format);
87         end
88 end
89
90 function save(filename, format)
91 end
92
93 function addparser(format, parser)
94         if format and parser then
95                 parsers[format] = parser;
96         end
97 end
98
99 -- _M needed to avoid name clash with local 'parsers'
100 function _M.parsers()
101         local p = {};
102         for format in pairs(parsers) do
103                 table.insert(p, format);
104         end
105         return p;
106 end
107
108 -- Built-in Lua parser
109 do
110         local loadstring, pcall, setmetatable = _G.loadstring, _G.pcall, _G.setmetatable;
111         local setfenv, rawget, tostring = _G.setfenv, _G.rawget, _G.tostring;
112         parsers.lua = {};
113         function parsers.lua.load(data, filename)
114                 local env;
115                 -- The ' = true' are needed so as not to set off __newindex when we assign the functions below
116                 env = setmetatable({
117                         Host = true, host = true, VirtualHost = true,
118                         Component = true, component = true,
119                         Include = true, include = true, RunScript = dofile }, {
120                                 __index = function (t, k)
121                                         return rawget(_G, k) or
122                                                 function (settings_table)
123                                                         config[__currenthost or "*"][k] = settings_table;
124                                                 end;
125                                 end,
126                                 __newindex = function (t, k, v)
127                                         set(env.__currenthost or "*", "core", k, v);
128                                 end
129                 });
130                 
131                 rawset(env, "__currenthost", "*") -- Default is global
132                 function env.VirtualHost(name)
133                         if rawget(config, name) and rawget(config[name].core, "component_module") then
134                                 error(format("Host %q clashes with previously defined %s Component %q, for services use a sub-domain like conference.%s",
135                                         name, config[name].core.component_module:gsub("^%a+$", { component = "external", muc = "MUC"}), name, name), 0);
136                         end
137                         rawset(env, "__currenthost", name);
138                         -- Needs at least one setting to logically exist :)
139                         set(name or "*", "core", "defined", true);
140                 end
141                 env.Host, env.host = env.VirtualHost, env.VirtualHost;
142                 
143                 function env.Component(name)
144                         if rawget(config, name) and rawget(config[name].core, "defined") and not rawget(config[name].core, "component_module") then
145                                 error(format("Component %q clashes with previously defined Host %q, for services use a sub-domain like conference.%s",
146                                         name, name, name), 0);
147                         end
148                         set(name, "core", "component_module", "component");
149                         -- Don't load the global modules by default
150                         set(name, "core", "load_global_modules", false);
151                         rawset(env, "__currenthost", name);
152         
153                         return function (module)
154                                         if type(module) == "string" then
155                                                 set(name, "core", "component_module", module);
156                                         end
157                                 end
158                 end
159                 env.component = env.Component;
160                 
161                 function env.Include(file)
162                         local f, err = io.open(file);
163                         if f then
164                                 local data = f:read("*a");
165                                 local ok, err = parsers.lua.load(data, file);
166                                 if not ok then error(err:gsub("%[string.-%]", file), 0); end
167                         end
168                         if not f then error("Error loading included "..file..": "..err, 0); end
169                         return f, err;
170                 end
171                 env.include = env.Include;
172                 
173                 local chunk, err = loadstring(data, "@"..filename);
174                 
175                 if not chunk then
176                         return nil, err;
177                 end
178                 
179                 setfenv(chunk, env);
180                 
181                 local ok, err = pcall(chunk);
182                 
183                 if not ok then
184                         return nil, err;
185                 end
186                 
187                 return true;
188         end
189         
190 end
191
192 return _M;