Commit e9040fae3d7aa49ba4536d281bec3a04b8b0dfc4
1 parent
e14c26d9
Added LuaExpat XML parsing library
git-svn-id: http://svn.net-core.org/repos/t-engine4@1297 51575b47-30f0-44d4-a5cc-537603b46e54
Showing
26 changed files
with
4876 additions
and
1 deletions
Too many changes to show.
To preserve performance only 26 of 26+ files are displayed.
... | ... | @@ -261,6 +261,15 @@ Now go and have some fun!]] |
261 | 261 | self.step:setKeyHandling() |
262 | 262 | self.step:setMouseHandling() |
263 | 263 | |
264 | + self.step.key:addBinds{ | |
265 | + -- Lua console | |
266 | + LUA_CONSOLE = function() | |
267 | + if config.settings.tome.cheat then | |
268 | + self:registerDialog(require("engine.DebugConsole").new()) | |
269 | + end | |
270 | + end, | |
271 | + } | |
272 | + | |
264 | 273 | if self.s_log then |
265 | 274 | local w, h = self.s_log:getSize() |
266 | 275 | self.mouse:registerZone(self.w - w, self.h - h, w, h, function(button) | ... | ... |
game/thirdparty/core/certmanager.lua
0 → 100644
1 | +local configmanager = require "core.configmanager"; | |
2 | +local log = require "util.logger".init("certmanager"); | |
3 | +local ssl = ssl; | |
4 | +local ssl_newcontext = ssl and ssl.newcontext; | |
5 | + | |
6 | +local setmetatable, tostring = setmetatable, tostring; | |
7 | + | |
8 | +local prosody = prosody; | |
9 | + | |
10 | +module "certmanager" | |
11 | + | |
12 | +-- These are the defaults if not overridden in the config | |
13 | +local default_ssl_ctx = { mode = "client", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none", options = "no_sslv2"; }; | |
14 | +local default_ssl_ctx_in = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none", options = "no_sslv2"; }; | |
15 | + | |
16 | +local default_ssl_ctx_mt = { __index = default_ssl_ctx }; | |
17 | +local default_ssl_ctx_in_mt = { __index = default_ssl_ctx_in }; | |
18 | + | |
19 | +-- Global SSL options if not overridden per-host | |
20 | +local default_ssl_config = configmanager.get("*", "core", "ssl"); | |
21 | + | |
22 | +function create_context(host, mode, config) | |
23 | + local ssl_config = config and config.core.ssl or default_ssl_config; | |
24 | + if ssl and ssl_config then | |
25 | + local ctx, err = ssl_newcontext(setmetatable(ssl_config, mode == "client" and default_ssl_ctx_mt or default_ssl_ctx_in_mt)); | |
26 | + if not ctx then | |
27 | + err = err or "invalid ssl config" | |
28 | + local file = err:match("^error loading (.-) %("); | |
29 | + if file then | |
30 | + if file == "private key" then | |
31 | + file = ssl_config.key or "your private key"; | |
32 | + elseif file == "certificate" then | |
33 | + file = ssl_config.certificate or "your certificate file"; | |
34 | + end | |
35 | + local reason = err:match("%((.+)%)$") or "some reason"; | |
36 | + if reason == "Permission denied" then | |
37 | + reason = "Check that the permissions allow Prosody to read this file."; | |
38 | + elseif reason == "No such file or directory" then | |
39 | + reason = "Check that the path is correct, and the file exists."; | |
40 | + elseif reason == "system lib" then | |
41 | + reason = "Previous error (see logs), or other system error."; | |
42 | + elseif reason == "(null)" or not reason then | |
43 | + reason = "Check that the file exists and the permissions are correct"; | |
44 | + else | |
45 | + reason = "Reason: "..tostring(reason):lower(); | |
46 | + end | |
47 | + log("error", "SSL/TLS: Failed to load %s: %s", file, reason); | |
48 | + else | |
49 | + log("error", "SSL/TLS: Error initialising for host %s: %s", host, err ); | |
50 | + end | |
51 | + ssl = false | |
52 | + end | |
53 | + return ctx, err; | |
54 | + end | |
55 | + return nil; | |
56 | +end | |
57 | + | |
58 | +function reload_ssl_config() | |
59 | + default_ssl_config = configmanager.get("*", "core", "ssl"); | |
60 | +end | |
61 | + | |
62 | +prosody.events.add_handler("config-reloaded", reload_ssl_config); | |
63 | + | |
64 | +return _M; | ... | ... |
game/thirdparty/core/componentmanager.lua
0 → 100644
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 | +local prosody = _G.prosody; | |
10 | +local log = require "util.logger".init("componentmanager"); | |
11 | +local certmanager = require "core.certmanager"; | |
12 | +local configmanager = require "core.configmanager"; | |
13 | +local modulemanager = require "core.modulemanager"; | |
14 | +local jid_split = require "util.jid".split; | |
15 | +local fire_event = require "core.eventmanager".fire_event; | |
16 | +local events_new = require "util.events".new; | |
17 | +local st = require "util.stanza"; | |
18 | +local prosody, hosts = prosody, prosody.hosts; | |
19 | +local ssl = ssl; | |
20 | +local uuid_gen = require "util.uuid".generate; | |
21 | + | |
22 | +local pairs, setmetatable, type, tostring = pairs, setmetatable, type, tostring; | |
23 | + | |
24 | +local components = {}; | |
25 | + | |
26 | +local disco_items = require "util.multitable".new(); | |
27 | +local NULL = {}; | |
28 | + | |
29 | +module "componentmanager" | |
30 | + | |
31 | +local function default_component_handler(origin, stanza) | |
32 | + log("warn", "Stanza being handled by default component; bouncing error for: %s", stanza:top_tag()); | |
33 | + if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then | |
34 | + origin.send(st.error_reply(stanza, "wait", "service-unavailable", "Component unavailable")); | |
35 | + end | |
36 | +end | |
37 | + | |
38 | +function load_enabled_components(config) | |
39 | + local defined_hosts = config or configmanager.getconfig(); | |
40 | + | |
41 | + for host, host_config in pairs(defined_hosts) do | |
42 | + if host ~= "*" and ((host_config.core.enabled == nil or host_config.core.enabled) and type(host_config.core.component_module) == "string") then | |
43 | + hosts[host] = create_component(host); | |
44 | + hosts[host].connected = false; | |
45 | + components[host] = default_component_handler; | |
46 | + local ok, err = modulemanager.load(host, host_config.core.component_module); | |
47 | + if not ok then | |
48 | + log("error", "Error loading %s component %s: %s", tostring(host_config.core.component_module), tostring(host), tostring(err)); | |
49 | + else | |
50 | + fire_event("component-activated", host, host_config); | |
51 | + log("debug", "Activated %s component: %s", host_config.core.component_module, host); | |
52 | + end | |
53 | + end | |
54 | + end | |
55 | +end | |
56 | + | |
57 | +if prosody and prosody.events then | |
58 | + prosody.events.add_handler("server-starting", load_enabled_components); | |
59 | +end | |
60 | + | |
61 | +function handle_stanza(origin, stanza) | |
62 | + local node, host = jid_split(stanza.attr.to); | |
63 | + local component = nil; | |
64 | + if host then | |
65 | + if node then component = components[node.."@"..host]; end -- hack to allow hooking node@server | |
66 | + if not component then component = components[host]; end | |
67 | + end | |
68 | + if component then | |
69 | + log("debug", "%s stanza being handled by component: %s", stanza.name, host); | |
70 | + component(origin, stanza, hosts[host]); | |
71 | + else | |
72 | + log("error", "Component manager recieved a stanza for a non-existing component: "..tostring(stanza)); | |
73 | + default_component_handler(origin, stanza); | |
74 | + end | |
75 | +end | |
76 | + | |
77 | +function create_component(host, component, events) | |
78 | + -- TODO check for host well-formedness | |
79 | + local ssl_ctx, ssl_ctx_in; | |
80 | + if host and ssl then | |
81 | + -- We need to find SSL context to use... | |
82 | + -- Discussion in prosody@ concluded that | |
83 | + -- 1 level back is usually enough by default | |
84 | + local base_host = host:gsub("^[^%.]+%.", ""); | |
85 | + if hosts[base_host] then | |
86 | + ssl_ctx = hosts[base_host].ssl_ctx; | |
87 | + ssl_ctx_in = hosts[base_host].ssl_ctx_in; | |
88 | + else | |
89 | + -- We have no cert, and no parent host to borrow a cert from | |
90 | + -- Use global/default cert if there is one | |
91 | + ssl_ctx = certmanager.create_context(host, "client"); | |
92 | + ssl_ctx_in = certmanager.create_context(host, "server"); | |
93 | + end | |
94 | + end | |
95 | + return { type = "component", host = host, connected = true, s2sout = {}, | |
96 | + ssl_ctx = ssl_ctx, ssl_ctx_in = ssl_ctx_in, events = events or events_new(), | |
97 | + dialback_secret = configmanager.get(host, "core", "dialback_secret") or uuid_gen() }; | |
98 | +end | |
99 | + | |
100 | +function register_component(host, component, session) | |
101 | + if not hosts[host] or (hosts[host].type == 'component' and not hosts[host].connected) then | |
102 | + local old_events = hosts[host] and hosts[host].events; | |
103 | + | |
104 | + components[host] = component; | |
105 | + hosts[host] = session or create_component(host, component, old_events); | |
106 | + | |
107 | + -- Add events object if not already one | |
108 | + if not hosts[host].events then | |
109 | + hosts[host].events = old_events or events_new(); | |
110 | + end | |
111 | + | |
112 | + if not hosts[host].dialback_secret then | |
113 | + hosts[host].dialback_secret = configmanager.get(host, "core", "dialback_secret") or uuid_gen(); | |
114 | + end | |
115 | + | |
116 | + -- add to disco_items | |
117 | + if not(host:find("@", 1, true) or host:find("/", 1, true)) and host:find(".", 1, true) then | |
118 | + disco_items:set(host:sub(host:find(".", 1, true)+1), host, true); | |
119 | + end | |
120 | + modulemanager.load(host, "dialback"); | |
121 | + modulemanager.load(host, "tls"); | |
122 | + log("debug", "component added: "..host); | |
123 | + return session or hosts[host]; | |
124 | + else | |
125 | + log("error", "Attempt to set component for existing host: "..host); | |
126 | + end | |
127 | +end | |
128 | + | |
129 | +function deregister_component(host) | |
130 | + if components[host] then | |
131 | + modulemanager.unload(host, "tls"); | |
132 | + modulemanager.unload(host, "dialback"); | |
133 | + hosts[host].connected = nil; | |
134 | + local host_config = configmanager.getconfig()[host]; | |
135 | + if host_config and ((host_config.core.enabled == nil or host_config.core.enabled) and type(host_config.core.component_module) == "string") then | |
136 | + -- Set default handler | |
137 | + components[host] = default_component_handler; | |
138 | + else | |
139 | + -- Component not in config, or disabled, remove | |
140 | + hosts[host] = nil; -- FIXME do proper unload of all modules and other cleanup before removing | |
141 | + components[host] = nil; | |
142 | + end | |
143 | + -- remove from disco_items | |
144 | + if not(host:find("@", 1, true) or host:find("/", 1, true)) and host:find(".", 1, true) then | |
145 | + disco_items:remove(host:sub(host:find(".", 1, true)+1), host); | |
146 | + end | |
147 | + log("debug", "component removed: "..host); | |
148 | + return true; | |
149 | + else | |
150 | + log("error", "Attempt to remove component for non-existing host: "..host); | |
151 | + end | |
152 | +end | |
153 | + | |
154 | +function set_component_handler(host, handler) | |
155 | + components[host] = handler; | |
156 | +end | |
157 | + | |
158 | +function get_children(host) | |
159 | + return disco_items:get(host) or NULL; | |
160 | +end | |
161 | + | |
162 | +return _M; | ... | ... |
game/thirdparty/core/configmanager.lua
0 → 100644
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 | +end | |
38 | + | |
39 | +function getconfig() | |
40 | + return config; | |
41 | +end | |
42 | + | |
43 | +function get(host, section, key) | |
44 | + local sec = config[host][section]; | |
45 | + if sec then | |
46 | + return sec[key]; | |
47 | + end | |
48 | + return nil; | |
49 | +end | |
50 | + | |
51 | +function set(host, section, key, value) | |
52 | + if host and section and key then | |
53 | + local hostconfig = rawget(config, host); | |
54 | + if not hostconfig then | |
55 | + hostconfig = rawset(config, host, setmetatable({}, host_mt))[host]; | |
56 | + end | |
57 | + if not rawget(hostconfig, section) then | |
58 | + hostconfig[section] = setmetatable({}, section_mt(section)); | |
59 | + end | |
60 | + hostconfig[section][key] = value; | |
61 | + return true; | |
62 | + end | |
63 | + return false; | |
64 | +end | |
65 | + | |
66 | +function load(filename, format) | |
67 | + format = format or filename:match("%w+$"); | |
68 | + | |
69 | + if parsers[format] and parsers[format].load then | |
70 | + local f, err = io.open(filename); | |
71 | + if f then | |
72 | + local ok, err = parsers[format].load(f:read("*a"), filename); | |
73 | + f:close(); | |
74 | + if ok then | |
75 | + eventmanager.fire_event("config-reloaded", { filename = filename, format = format }); | |
76 | + end | |
77 | + return ok, "parser", err; | |
78 | + end | |
79 | + return f, "file", err; | |
80 | + end | |
81 | + | |
82 | + if not format then | |
83 | + return nil, "file", "no parser specified"; | |
84 | + else | |
85 | + return nil, "file", "no parser for "..(format); | |
86 | + end | |
87 | +end | |
88 | + | |
89 | +function save(filename, format) | |
90 | +end | |
91 | + | |
92 | +function addparser(format, parser) | |
93 | + if format and parser then | |
94 | + parsers[format] = parser; | |
95 | + end | |
96 | +end | |
97 | + | |
98 | +-- _M needed to avoid name clash with local 'parsers' | |
99 | +function _M.parsers() | |
100 | + local p = {}; | |
101 | + for format in pairs(parsers) do | |
102 | + table.insert(p, format); | |
103 | + end | |
104 | + return p; | |
105 | +end | |
106 | + | |
107 | +-- Built-in Lua parser | |
108 | +do | |
109 | + local loadstring, pcall, setmetatable = _G.loadstring, _G.pcall, _G.setmetatable; | |
110 | + local setfenv, rawget, tostring = _G.setfenv, _G.rawget, _G.tostring; | |
111 | + parsers.lua = {}; | |
112 | + function parsers.lua.load(data, filename) | |
113 | + local env; | |
114 | + -- The ' = true' are needed so as not to set off __newindex when we assign the functions below | |
115 | + env = setmetatable({ Host = true, host = true, VirtualHost = true, Component = true, component = true, | |
116 | + Include = true, include = true, RunScript = dofile }, { __index = function (t, k) | |
117 | + return rawget(_G, k) or | |
118 | + function (settings_table) | |
119 | + config[__currenthost or "*"][k] = settings_table; | |
120 | + end; | |
121 | + end, | |
122 | + __newindex = function (t, k, v) | |
123 | + set(env.__currenthost or "*", "core", k, v); | |
124 | + end}); | |
125 | + | |
126 | + rawset(env, "__currenthost", "*") -- Default is global | |
127 | + function env.VirtualHost(name) | |
128 | + if rawget(config, name) and rawget(config[name].core, "component_module") then | |
129 | + error(format("Host %q clashes with previously defined %s Component %q, for services use a sub-domain like conference.%s", | |
130 | + name, config[name].core.component_module:gsub("^%a+$", { component = "external", muc = "MUC"}), name, name), 0); | |
131 | + end | |
132 | + rawset(env, "__currenthost", name); | |
133 | + -- Needs at least one setting to logically exist :) | |
134 | + set(name or "*", "core", "defined", true); | |
135 | + end | |
136 | + env.Host, env.host = env.VirtualHost, env.VirtualHost; | |
137 | + | |
138 | + function env.Component(name) | |
139 | + if rawget(config, name) and rawget(config[name].core, "defined") and not rawget(config[name].core, "component_module") then | |
140 | + error(format("Component %q clashes with previously defined Host %q, for services use a sub-domain like conference.%s", | |
141 | + name, name, name), 0); | |
142 | + end | |
143 | + set(name, "core", "component_module", "component"); | |
144 | + -- Don't load the global modules by default | |
145 | + set(name, "core", "load_global_modules", false); | |
146 | + rawset(env, "__currenthost", name); | |
147 | + | |
148 | + return function (module) | |
149 | + if type(module) == "string" then | |
150 | + set(name, "core", "component_module", module); | |
151 | + end | |
152 | + end | |
153 | + end | |
154 | + env.component = env.Component; | |
155 | + | |
156 | + function env.Include(file) | |
157 | + local f, err = io.open(file); | |
158 | + if f then | |
159 | + local data = f:read("*a"); | |
160 | + local ok, err = parsers.lua.load(data, file); | |
161 | + if not ok then error(err:gsub("%[string.-%]", file), 0); end | |
162 | + end | |
163 | + if not f then error("Error loading included "..file..": "..err, 0); end | |
164 | + return f, err; | |
165 | + end | |
166 | + env.include = env.Include; | |
167 | + | |
168 | + local chunk, err = loadstring(data, "@"..filename); | |
169 | + | |
170 | + if not chunk then | |
171 | + return nil, err; | |
172 | + end | |
173 | + | |
174 | + setfenv(chunk, env); | |
175 | + | |
176 | + local ok, err = pcall(chunk); | |
177 | + | |
178 | + if not ok then | |
179 | + return nil, err; | |
180 | + end | |
181 | + | |
182 | + return true; | |
183 | + end | |
184 | + | |
185 | +end | |
186 | + | |
187 | +return _M; | ... | ... |
game/thirdparty/core/eventmanager.lua
0 → 100644
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 | +local t_insert = table.insert; | |
11 | +local ipairs = ipairs; | |
12 | + | |
13 | +module "eventmanager" | |
14 | + | |
15 | +local event_handlers = {}; | |
16 | + | |
17 | +function add_event_hook(name, handler) | |
18 | + if not event_handlers[name] then | |
19 | + event_handlers[name] = {}; | |
20 | + end | |
21 | + t_insert(event_handlers[name] , handler); | |
22 | +end | |
23 | + | |
24 | +function fire_event(name, ...) | |
25 | + local event_handlers = event_handlers[name]; | |
26 | + if event_handlers then | |
27 | + for name, handler in ipairs(event_handlers) do | |
28 | + handler(...); | |
29 | + end | |
30 | + end | |
31 | +end | |
32 | + | |
33 | +return _M; | |
\ No newline at end of file | ... | ... |
game/thirdparty/core/hostmanager.lua
0 → 100644
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 | +local ssl = ssl | |
10 | + | |
11 | +local hosts = hosts; | |
12 | +local certmanager = require "core.certmanager"; | |
13 | +local configmanager = require "core.configmanager"; | |
14 | +local eventmanager = require "core.eventmanager"; | |
15 | +local modulemanager = require "core.modulemanager"; | |
16 | +local events_new = require "util.events".new; | |
17 | + | |
18 | +local uuid_gen = require "util.uuid".generate; | |
19 | + | |
20 | +if not _G.prosody.incoming_s2s then | |
21 | + require "core.s2smanager"; | |
22 | +end | |
23 | +local incoming_s2s = _G.prosody.incoming_s2s; | |
24 | + | |
25 | +local log = require "util.logger".init("hostmanager"); | |
26 | + | |
27 | +local pairs, setmetatable = pairs, setmetatable; | |
28 | + | |
29 | +module "hostmanager" | |
30 | + | |
31 | +local hosts_loaded_once; | |
32 | + | |
33 | +local function load_enabled_hosts(config) | |
34 | + local defined_hosts = config or configmanager.getconfig(); | |
35 | + local activated_any_host; | |
36 | + | |
37 | + for host, host_config in pairs(defined_hosts) do | |
38 | + if host ~= "*" and host_config.core.enabled ~= false and not host_config.core.component_module then | |
39 | + activated_any_host = true; | |
40 | + activate(host, host_config); | |
41 | + end | |
42 | + end | |
43 | + | |
44 | + if not activated_any_host then | |
45 | + log("error", "No active VirtualHost entries in the config file. This may cause unexpected behaviour as no modules will be loaded."); | |
46 | + end | |
47 | + | |
48 | + eventmanager.fire_event("hosts-activated", defined_hosts); | |
49 | + hosts_loaded_once = true; | |
50 | +end | |
51 | + | |
52 | +eventmanager.add_event_hook("server-starting", load_enabled_hosts); | |
53 | + | |
54 | +function activate(host, host_config) | |
55 | + hosts[host] = {type = "local", connected = true, sessions = {}, | |
56 | + host = host, s2sout = {}, events = events_new(), | |
57 | + disallow_s2s = configmanager.get(host, "core", "disallow_s2s") | |
58 | + or (configmanager.get(host, "core", "anonymous_login") | |
59 | + and (configmanager.get(host, "core", "disallow_s2s") ~= false)); | |
60 | + dialback_secret = configmanager.get(host, "core", "dialback_secret") or uuid_gen(); | |
61 | + }; | |
62 | + for option_name in pairs(host_config.core) do | |
63 | + if option_name:match("_ports$") or option_name:match("_interface$") then | |
64 | + log("warn", "%s: Option '%s' has no effect for virtual hosts - put it in the server-wide section instead", host, option_name); | |
65 | + end | |
66 | + end | |
67 | + | |
68 | + hosts[host].ssl_ctx = certmanager.create_context(host, "client", host_config); -- for outgoing connections | |
69 | + hosts[host].ssl_ctx_in = certmanager.create_context(host, "server", host_config); -- for incoming connections | |
70 | + | |
71 | + log((hosts_loaded_once and "info") or "debug", "Activated host: %s", host); | |
72 | + eventmanager.fire_event("host-activated", host, host_config); | |
73 | +end | |
74 | + | |
75 | +function deactivate(host, reason) | |
76 | + local host_session = hosts[host]; | |
77 | + log("info", "Deactivating host: %s", host); | |
78 | + eventmanager.fire_event("host-deactivating", host, host_session); | |
79 | + | |
80 | + reason = reason or { condition = "host-gone", text = "This server has stopped serving "..host }; | |
81 | + | |
82 | + -- Disconnect local users, s2s connections | |
83 | + if host_session.sessions then | |
84 | + for username, user in pairs(host_session.sessions) do | |
85 | + for resource, session in pairs(user.sessions) do | |
86 | + log("debug", "Closing connection for %s@%s/%s", username, host, resource); | |
87 | + session:close(reason); | |
88 | + end | |
89 | + end | |
90 | + end | |
91 | + if host_session.s2sout then | |
92 | + for remotehost, session in pairs(host_session.s2sout) do | |
93 | + if session.close then | |
94 | + log("debug", "Closing outgoing connection to %s", remotehost); | |
95 | + if session.srv_hosts then session.srv_hosts = nil; end | |
96 | + session:close(reason); | |
97 | + end | |
98 | + end | |
99 | + end | |
100 | + for remote_session in pairs(incoming_s2s) do | |
101 | + if remote_session.to_host == host then | |
102 | + log("debug", "Closing incoming connection from %s", remote_session.from_host or "<unknown>"); | |
103 | + remote_session:close(reason); | |
104 | + end | |
105 | + end | |
106 | + | |
107 | + if host_session.modules then | |
108 | + for module in pairs(host_session.modules) do | |
109 | + modulemanager.unload(host, module); | |
110 | + end | |
111 | + end | |
112 | + | |
113 | + hosts[host] = nil; | |
114 | + eventmanager.fire_event("host-deactivated", host); | |
115 | + log("info", "Deactivated host: %s", host); | |
116 | +end | |
117 | + | |
118 | +function getconfig(name) | |
119 | +end | |
120 | + | |
121 | +return _M; | ... | ... |
game/thirdparty/core/loggingmanager.lua
0 → 100644
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 | +local format, rep = string.format, string.rep; | |
11 | +local pcall = pcall; | |
12 | +local debug = debug; | |
13 | +local tostring, setmetatable, rawset, pairs, ipairs, type = | |
14 | + tostring, setmetatable, rawset, pairs, ipairs, type; | |
15 | +local io_open, io_write = io.open, io.write; | |
16 | +local math_max, rep = math.max, string.rep; | |
17 | +local os_date, os_getenv = os.date, os.getenv; | |
18 | +local getstyle, getstring = require "util.termcolours".getstyle, require "util.termcolours".getstring; | |
19 | + | |
20 | +if os.getenv("__FLUSH_LOG") then | |
21 | + local io_flush = io.flush; | |
22 | + local _io_write = io_write; | |
23 | + io_write = function(...) _io_write(...); io_flush(); end | |
24 | +end | |
25 | + | |
26 | +local config = require "core.configmanager"; | |
27 | +local eventmanager = require "core.eventmanager"; | |
28 | +local logger = require "util.logger"; | |
29 | +local debug_mode = config.get("*", "core", "debug"); | |
30 | + | |
31 | +_G.log = logger.init("general"); | |
32 | + | |
33 | +module "loggingmanager" | |
34 | + | |
35 | +-- The log config used if none specified in the config file | |
36 | +local default_logging = { { to = "console" , levels = { min = (debug_mode and "debug") or "info" } } }; | |
37 | +local default_file_logging = { { to = "file", levels = { min = (debug_mode and "debug") or "info" }, timestamps = true } }; | |
38 | +local default_timestamp = "%b %d %H:%M:%S"; | |
39 | +-- The actual config loggingmanager is using | |
40 | +local logging_config = config.get("*", "core", "log") or default_logging; | |
41 | + | |
42 | +local apply_sink_rules; | |
43 | +local log_sink_types = setmetatable({}, { __newindex = function (t, k, v) rawset(t, k, v); apply_sink_rules(k); end; }); | |
44 | +local get_levels; | |
45 | +local logging_levels = { "debug", "info", "warn", "error", "critical" } | |
46 | + | |
47 | +-- Put a rule into action. Requires that the sink type has already been registered. | |
48 | +-- This function is called automatically when a new sink type is added [see apply_sink_rules()] | |
49 | +local function add_rule(sink_config) | |
50 | + local sink_maker = log_sink_types[sink_config.to]; | |
51 | + if sink_maker then | |
52 | + if sink_config.levels and not sink_config.source then | |
53 | + -- Create sink | |
54 | + local sink = sink_maker(sink_config); | |
55 | + | |
56 | + -- Set sink for all chosen levels | |
57 | + for level in pairs(get_levels(sink_config.levels)) do | |
58 | + logger.add_level_sink(level, sink); | |
59 | + end | |
60 | + elseif sink_config.source and not sink_config.levels then | |
61 | + logger.add_name_sink(sink_config.source, sink_maker(sink_config)); | |
62 | + elseif sink_config.source and sink_config.levels then | |
63 | + local levels = get_levels(sink_config.levels); | |
64 | + local sink = sink_maker(sink_config); | |
65 | + logger.add_name_sink(sink_config.source, | |
66 | + function (name, level, ...) | |
67 | + if levels[level] then | |
68 | + return sink(name, level, ...); | |
69 | + end | |
70 | + end); | |
71 | + else | |
72 | + -- All sources | |
73 | + -- Create sink | |
74 | + local sink = sink_maker(sink_config); | |
75 | + | |
76 | + -- Set sink for all levels | |
77 | + for _, level in pairs(logging_levels) do | |
78 | + logger.add_level_sink(level, sink); | |
79 | + end | |
80 | + end | |
81 | + else | |
82 | + -- No such sink type | |
83 | + end | |
84 | +end | |
85 | + | |
86 | +-- Search for all rules using a particular sink type, and apply | |
87 | +-- them. Called automatically when a new sink type is added to | |
88 | +-- the log_sink_types table. | |
89 | +function apply_sink_rules(sink_type) | |
90 | + if type(logging_config) == "table" then | |
91 | + for _, sink_config in pairs(logging_config) do | |
92 | + if sink_config.to == sink_type then | |
93 | + add_rule(sink_config); | |
94 | + end | |
95 | + end | |
96 | + elseif type(logging_config) == "string" and (not logging_config:match("^%*")) and sink_type == "file" then | |
97 | + -- User specified simply a filename, and the "file" sink type | |
98 | + -- was just added | |
99 | + for _, sink_config in pairs(default_file_logging) do | |
100 | + sink_config.filename = logging_config; | |
101 | + add_rule(sink_config); | |
102 | + sink_config.filename = nil; | |
103 | + end | |
104 | + elseif type(logging_config) == "string" and logging_config:match("^%*(.+)") == sink_type then | |
105 | + -- Log all levels (debug+) to this sink | |
106 | + add_rule({ levels = { min = "debug" }, to = sink_type }); | |
107 | + end | |
108 | +end | |
109 | + | |
110 | + | |
111 | + | |
112 | +--- Helper function to get a set of levels given a "criteria" table | |
113 | +function get_levels(criteria, set) | |
114 | + set = set or {}; | |
115 | + if type(criteria) == "string" then | |
116 | + set[criteria] = true; | |
117 | + return set; | |
118 | + end | |
119 | + local min, max = criteria.min, criteria.max; | |
120 | + if min or max then | |
121 | + local in_range; | |
122 | + for _, level in ipairs(logging_levels) do | |
123 | + if min == level then | |
124 | + set[level] = true; | |
125 | + in_range = true; | |
126 | + elseif max == level then | |
127 | + set[level] = true; | |
128 | + return set; | |
129 | + elseif in_range then | |
130 | + set[level] = true; | |
131 | + end | |
132 | + end | |
133 | + end | |
134 | + | |
135 | + for _, level in ipairs(criteria) do | |
136 | + set[level] = true; | |
137 | + end | |
138 | + return set; | |
139 | +end | |
140 | + | |
141 | +--- Definition of built-in logging sinks --- | |
142 | + | |
143 | +-- Null sink, must enter log_sink_types *first* | |
144 | +function log_sink_types.nowhere() | |
145 | + return function () return false; end; | |
146 | +end | |
147 | + | |
148 | +-- Column width for "source" (used by stdout and console) | |
149 | +local sourcewidth = 20; | |
150 | + | |
151 | +function log_sink_types.stdout() | |
152 | + local timestamps = config.timestamps; | |
153 | + | |
154 | + if timestamps == true then | |
155 | + timestamps = default_timestamp; -- Default format | |
156 | + end | |
157 | + | |
158 | + return function (name, level, message, ...) | |
159 | + sourcewidth = math_max(#name+2, sourcewidth); | |
160 | + local namelen = #name; | |
161 | + if timestamps then | |
162 | + io_write(os_date(timestamps), " "); | |
163 | + end | |
164 | + if ... then | |
165 | + io_write(name, rep(" ", sourcewidth-namelen), level, "\t", format(message, ...), "\n"); | |
166 | + else | |
167 | + io_write(name, rep(" ", sourcewidth-namelen), level, "\t", message, "\n"); | |
168 | + end | |
169 | + end | |
170 | +end | |
171 | + | |
172 | +do | |
173 | + local do_pretty_printing = not os_getenv("WINDIR"); | |
174 | + | |
175 | + local logstyles = {}; | |
176 | + if do_pretty_printing then | |
177 | + logstyles["info"] = getstyle("bold"); | |
178 | + logstyles["warn"] = getstyle("bold", "yellow"); | |
179 | + logstyles["error"] = getstyle("bold", "red"); | |
180 | + end | |
181 | + function log_sink_types.console(config) | |
182 | + -- Really if we don't want pretty colours then just use plain stdout | |
183 | + if not do_pretty_printing then | |
184 | + return log_sink_types.stdout(config); | |
185 | + end | |
186 | + | |
187 | + local timestamps = config.timestamps; | |
188 | + | |
189 | + if timestamps == true then | |
190 | + timestamps = default_timestamp; -- Default format | |
191 | + end | |
192 | + | |
193 | + return function (name, level, message, ...) | |
194 | + sourcewidth = math_max(#name+2, sourcewidth); | |
195 | + local namelen = #name; | |
196 | + | |
197 | + if timestamps then | |
198 | + io_write(os_date(timestamps), " "); | |
199 | + end | |
200 | + if ... then | |
201 | + io_write(name, rep(" ", sourcewidth-namelen), getstring(logstyles[level], level), "\t", format(message, ...), "\n"); | |
202 | + else | |
203 | + io_write(name, rep(" ", sourcewidth-namelen), getstring(logstyles[level], level), "\t", message, "\n"); | |
204 | + end | |
205 | + end | |
206 | + end | |
207 | +end | |
208 | + | |
209 | +local empty_function = function () end; | |
210 | +function log_sink_types.file(config) | |
211 | + local log = config.filename; | |
212 | + local logfile = io_open(log, "a+"); | |
213 | + if not logfile then | |
214 | + return empty_function; | |
215 | + end | |
216 | + local write, flush = logfile.write, logfile.flush; | |
217 | + | |
218 | + eventmanager.add_event_hook("reopen-log-files", function () | |
219 | + if logfile then | |
220 | + logfile:close(); | |
221 | + end | |
222 | + logfile = io_open(log, "a+"); | |
223 | + if not logfile then | |
224 | + write, flush = empty_function, empty_function; | |
225 | + else | |
226 | + write, flush = logfile.write, logfile.flush; | |
227 | + end | |
228 | + end); | |
229 | + | |
230 | + local timestamps = config.timestamps; | |
231 | + | |
232 | + if timestamps == nil or timestamps == true then | |
233 | + timestamps = default_timestamp; -- Default format | |
234 | + end | |
235 | + | |
236 | + return function (name, level, message, ...) | |
237 | + if timestamps then | |
238 | + write(logfile, os_date(timestamps), " "); | |
239 | + end | |
240 | + if ... then | |
241 | + write(logfile, name, "\t", level, "\t", format(message, ...), "\n"); | |
242 | + else | |
243 | + write(logfile, name, "\t" , level, "\t", message, "\n"); | |
244 | + end | |
245 | + flush(logfile); | |
246 | + end; | |
247 | +end | |
248 | + | |
249 | +function register_sink_type(name, sink_maker) | |
250 | + local old_sink_maker = log_sink_types[name]; | |
251 | + log_sink_types[name] = sink_maker; | |
252 | + return old_sink_maker; | |
253 | +end | |
254 | + | |
255 | +return _M; | ... | ... |
game/thirdparty/core/modulemanager.lua
0 → 100644
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 | +local plugin_dir = CFG_PLUGINDIR or "./plugins/"; | |
10 | + | |
11 | +local logger = require "util.logger"; | |
12 | +local log = logger.init("modulemanager"); | |
13 | +local eventmanager = require "core.eventmanager"; | |
14 | +local config = require "core.configmanager"; | |
15 | +local multitable_new = require "util.multitable".new; | |
16 | +local st = require "util.stanza"; | |
17 | +local pluginloader = require "util.pluginloader"; | |
18 | + | |
19 | +local hosts = hosts; | |
20 | +local prosody = prosody; | |
21 | + | |
22 | +local loadfile, pcall, xpcall = loadfile, pcall, xpcall; | |
23 | +local setmetatable, setfenv, getfenv = setmetatable, setfenv, getfenv; | |
24 | +local pairs, ipairs = pairs, ipairs; | |
25 | +local t_insert, t_concat = table.insert, table.concat; | |
26 | +local type = type; | |
27 | +local next = next; | |
28 | +local rawget = rawget; | |
29 | +local error = error; | |
30 | +local tostring, tonumber = tostring, tonumber; | |
31 | + | |
32 | +local debug_traceback = debug.traceback; | |
33 | +local unpack, select = unpack, select; | |
34 | +pcall = function(f, ...) | |
35 | + local n = select("#", ...); | |
36 | + local params = {...}; | |
37 | + return xpcall(function() f(unpack(params, 1, n)) end, function(e) return tostring(e).."\n"..debug_traceback(); end); | |
38 | +end | |
39 | + | |
40 | +local array, set = require "util.array", require "util.set"; | |
41 | + | |
42 | +local autoload_modules = {"presence", "message", "iq"}; | |
43 | + | |
44 | +-- We need this to let modules access the real global namespace | |
45 | +local _G = _G; | |
46 | + | |
47 | +module "modulemanager" | |
48 | + | |
49 | +api = {}; | |
50 | +local api = api; -- Module API container | |
51 | + | |
52 | +local modulemap = { ["*"] = {} }; | |
53 | + | |
54 | +local stanza_handlers = multitable_new(); | |
55 | +local handler_info = {}; | |
56 | + | |
57 | +local modulehelpers = setmetatable({}, { __index = _G }); | |
58 | + | |
59 | +local handler_table = multitable_new(); | |
60 | +local hooked = multitable_new(); | |
61 | +local hooks = multitable_new(); | |
62 | +local event_hooks = multitable_new(); | |
63 | + | |
64 | +local NULL = {}; | |
65 | + | |
66 | +-- Load modules when a host is activated | |
67 | +function load_modules_for_host(host) | |
68 | + local disabled_set = {}; | |
69 | + local modules_disabled = config.get(host, "core", "modules_disabled"); | |
70 | + if modules_disabled then | |
71 | + for _, module in ipairs(modules_disabled) do | |
72 | + disabled_set[module] = true; | |
73 | + end | |
74 | + end | |
75 | + | |
76 | + -- Load auto-loaded modules for this host | |
77 | + if hosts[host].type == "local" then | |
78 | + for _, module in ipairs(autoload_modules) do | |
79 | + if not disabled_set[module] then | |
80 | + load(host, module); | |
81 | + end | |
82 | + end | |
83 | + end | |
84 | + | |
85 | + -- Load modules from global section | |
86 | + if config.get(host, "core", "load_global_modules") ~= false then | |
87 | + local modules_enabled = config.get("*", "core", "modules_enabled"); | |
88 | + if modules_enabled then | |
89 | + for _, module in ipairs(modules_enabled) do | |
90 | + if not disabled_set[module] and not is_loaded(host, module) then | |
91 | + load(host, module); | |
92 | + end | |
93 | + end | |
94 | + end | |
95 | + end | |
96 | + | |
97 | + -- Load modules from just this host | |
98 | + local modules_enabled = config.get(host, "core", "modules_enabled"); | |
99 | + if modules_enabled and modules_enabled ~= config.get("*", "core", "modules_enabled") then | |
100 | + for _, module in pairs(modules_enabled) do | |
101 | + if not is_loaded(host, module) then | |
102 | + load(host, module); | |
103 | + end | |
104 | + end | |
105 | + end | |
106 | +end | |
107 | +eventmanager.add_event_hook("host-activated", load_modules_for_host); | |
108 | +eventmanager.add_event_hook("component-activated", load_modules_for_host); | |
109 | +-- | |
110 | + | |
111 | +function load(host, module_name, config) | |
112 | + if not (host and module_name) then | |
113 | + return nil, "insufficient-parameters"; | |
114 | + end | |
115 | + | |
116 | + if not modulemap[host] then | |
117 | + modulemap[host] = {}; | |
118 | + end | |
119 | + | |
120 | + if modulemap[host][module_name] then | |
121 | + log("warn", "%s is already loaded for %s, so not loading again", module_name, host); | |
122 | + return nil, "module-already-loaded"; | |
123 | + elseif modulemap["*"][module_name] then | |
124 | + return nil, "global-module-already-loaded"; | |
125 | + end | |
126 | + | |
127 | + | |
128 | + local mod, err = pluginloader.load_code(module_name); | |
129 | + if not mod then | |
130 | + log("error", "Unable to load module '%s': %s", module_name or "nil", err or "nil"); | |
131 | + return nil, err; | |
132 | + end | |
133 | + | |
134 | + local _log = logger.init(host..":"..module_name); | |
135 | + local api_instance = setmetatable({ name = module_name, host = host, config = config, _log = _log, log = function (self, ...) return _log(...); end }, { __index = api }); | |
136 | + | |
137 | + local pluginenv = setmetatable({ module = api_instance }, { __index = _G }); | |
138 | + api_instance.environment = pluginenv; | |
139 | + | |
140 | + setfenv(mod, pluginenv); | |
141 | + if not hosts[host] then | |
142 | + local create_component = _G.require "core.componentmanager".create_component; | |
143 | + hosts[host] = create_component(host); | |
144 | + hosts[host].connected = false; | |
145 | + log("debug", "Created new component: %s", host); | |
146 | + end | |
147 | + hosts[host].modules = modulemap[host]; | |
148 | + modulemap[host][module_name] = pluginenv; | |
149 | + | |
150 | + local success, err = pcall(mod); | |
151 | + if success then | |
152 | + if module_has_method(pluginenv, "load") then | |
153 | + success, err = call_module_method(pluginenv, "load"); | |
154 | + if not success then | |
155 | + log("warn", "Error loading module '%s' on '%s': %s", module_name, host, err or "nil"); | |
156 | + end | |
157 | + end | |
158 | + | |
159 | + -- Use modified host, if the module set one | |
160 | + if api_instance.host == "*" and host ~= "*" then | |
161 | + modulemap[host][module_name] = nil; | |
162 | + modulemap["*"][module_name] = pluginenv; | |
163 | + api_instance:set_global(); | |
164 | + end | |
165 | + else | |
166 | + log("error", "Error initializing module '%s' on '%s': %s", module_name, host, err or "nil"); | |
167 | + end | |
168 | + if success then | |
169 | + (hosts[api_instance.host] or prosody).events.fire_event("module-loaded", { module = module_name, host = host }); | |
170 | + return true; | |
171 | + else -- load failed, unloading | |
172 | + unload(api_instance.host, module_name); | |
173 | + return nil, err; | |
174 | + end | |
175 | +end | |
176 | + | |
177 | +function get_module(host, name) | |
178 | + return modulemap[host] and modulemap[host][name]; | |
179 | +end | |
180 | + | |
181 | +function is_loaded(host, name) | |
182 | + return modulemap[host] and modulemap[host][name] and true; | |
183 | +end | |
184 | + | |
185 | +function unload(host, name, ...) | |
186 | + local mod = get_module(host, name); | |
187 | + if not mod then return nil, "module-not-loaded"; end | |
188 | + | |
189 | + if module_has_method(mod, "unload") then | |
190 | + local ok, err = call_module_method(mod, "unload"); | |
191 | + if (not ok) and err then | |
192 | + log("warn", "Non-fatal error unloading module '%s' on '%s': %s", name, host, err); | |
193 | + end | |
194 | + end | |
195 | + local params = handler_table:get(host, name); -- , {module.host, origin_type, tag, xmlns} | |
196 | + for _, param in pairs(params or NULL) do | |
197 | + local handlers = stanza_handlers:get(param[1], param[2], param[3], param[4]); | |
198 | + if handlers then | |
199 | + handler_info[handlers[1]] = nil; | |
200 | + stanza_handlers:remove(param[1], param[2], param[3], param[4]); | |
201 | + end | |
202 | + end | |
203 | + event_hooks:remove(host, name); | |
204 | + -- unhook event handlers hooked by module:hook | |
205 | + for event, handlers in pairs(hooks:get(host, name) or NULL) do | |
206 | + for handler in pairs(handlers or NULL) do | |
207 | + (hosts[host] or prosody).events.remove_handler(event, handler); | |
208 | + end | |
209 | + end | |
210 | + hooks:remove(host, name); | |
211 | + if mod.module.items then -- remove items | |
212 | + for key,t in pairs(mod.module.items) do | |
213 | + for i = #t,1,-1 do | |
214 | + local value = t[i]; | |
215 | + t[i] = nil; | |
216 | + hosts[host].events.fire_event("item-removed/"..key, {source = self, item = value}); | |
217 | + end | |
218 | + end | |
219 | + end | |
220 | + modulemap[host][name] = nil; | |
221 | + (hosts[host] or prosody).events.fire_event("module-unloaded", { module = name, host = host }); | |
222 | + return true; | |
223 | +end | |
224 | + | |
225 | +function reload(host, name, ...) | |
226 | + local mod = get_module(host, name); | |
227 | + if not mod then return nil, "module-not-loaded"; end | |
228 | + | |
229 | + local _mod, err = pluginloader.load_code(name); -- checking for syntax errors | |
230 | + if not _mod then | |
231 | + log("error", "Unable to load module '%s': %s", name or "nil", err or "nil"); | |
232 | + return nil, err; | |
233 | + end | |
234 | + | |
235 | + local saved; | |
236 | + | |
237 | + if module_has_method(mod, "save") then | |
238 | + local ok, ret, err = call_module_method(mod, "save"); | |
239 | + if ok then | |
240 | + saved = ret; | |
241 | + else | |
242 | + log("warn", "Error saving module '%s:%s' state: %s", host, module, ret); | |
243 | + if not config.get(host, "core", "force_module_reload") then | |
244 | + log("warn", "Aborting reload due to error, set force_module_reload to ignore this"); | |
245 | + return nil, "save-state-failed"; | |
246 | + else | |
247 | + log("warn", "Continuing with reload (using the force)"); | |
248 | + end | |
249 | + end | |
250 | + end | |
251 | + | |
252 | + unload(host, name, ...); | |
253 | + local ok, err = load(host, name, ...); | |
254 | + if ok then | |
255 | + mod = get_module(host, name); | |
256 | + if module_has_method(mod, "restore") then | |
257 | + local ok, err = call_module_method(mod, "restore", saved or {}) | |
258 | + if (not ok) and err then | |
259 | + log("warn", "Error restoring module '%s' from '%s': %s", name, host, err); | |
260 | + end | |
261 | + end | |
262 | + return true; | |
263 | + end | |
264 | + return ok, err; | |
265 | +end | |
266 | + | |
267 | +function handle_stanza(host, origin, stanza) | |
268 | + local name, xmlns, origin_type = stanza.name, stanza.attr.xmlns or "jabber:client", origin.type; | |
269 | + if name == "iq" and xmlns == "jabber:client" then | |
270 | + if stanza.attr.type == "get" or stanza.attr.type == "set" then | |
271 | + xmlns = stanza.tags[1].attr.xmlns or "jabber:client"; | |
272 | + log("debug", "Stanza of type %s from %s has xmlns: %s", name, origin_type, xmlns); | |
273 | + else | |
274 | + log("debug", "Discarding %s from %s of type: %s", name, origin_type, stanza.attr.type); | |
275 | + return true; | |
276 | + end | |
277 | + end | |
278 | + local handlers = stanza_handlers:get(host, origin_type, name, xmlns); | |
279 | + if not handlers then handlers = stanza_handlers:get("*", origin_type, name, xmlns); end | |
280 | + if handlers then | |
281 | + log("debug", "Passing stanza to mod_%s", handler_info[handlers[1]].name); | |
282 | + (handlers[1])(origin, stanza); | |
283 | + return true; | |
284 | + else | |
285 | + if stanza.attr.xmlns == nil then | |
286 | + log("debug", "Unhandled %s stanza: %s; xmlns=%s", origin.type, stanza.name, xmlns); -- we didn't handle it | |
287 | + if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then | |
288 | + origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); | |
289 | + end | |
290 | + elseif not((name == "features" or name == "error") and xmlns == "http://etherx.jabber.org/streams") then -- FIXME remove check once we handle S2S features | |
291 | + log("warn", "Unhandled %s stream element: %s; xmlns=%s: %s", origin.type, stanza.name, xmlns, tostring(stanza)); -- we didn't handle it | |
292 | + origin:close("unsupported-stanza-type"); | |
293 | + end | |
294 | + end | |
295 | +end | |
296 | + | |
297 | +function module_has_method(module, method) | |
298 | + return type(module.module[method]) == "function"; | |
299 | +end | |
300 | + | |
301 | +function call_module_method(module, method, ...) | |
302 | + if module_has_method(module, method) then | |
303 | + local f = module.module[method]; | |
304 | + return pcall(f, ...); | |
305 | + else | |
306 | + return false, "no-such-method"; | |
307 | + end | |
308 | +end | |
309 | + | |
310 | +----- API functions exposed to modules ----------- | |
311 | +-- Must all be in api.* | |
312 | + | |
313 | +-- Returns the name of the current module | |
314 | +function api:get_name() | |
315 | + return self.name; | |
316 | +end | |
317 | + | |
318 | +-- Returns the host that the current module is serving | |
319 | +function api:get_host() | |
320 | + return self.host; | |
321 | +end | |
322 | + | |
323 | +function api:get_host_type() | |
324 | + return hosts[self.host].type; | |
325 | +end | |
326 | + | |
327 | +function api:set_global() | |
328 | + self.host = "*"; | |
329 | + -- Update the logger | |
330 | + local _log = logger.init("mod_"..self.name); | |
331 | + self.log = function (self, ...) return _log(...); end; | |
332 | + self._log = _log; | |
333 | +end | |
334 | + | |
335 | +local function _add_handler(module, origin_type, tag, xmlns, handler) | |
336 | + local handlers = stanza_handlers:get(module.host, origin_type, tag, xmlns); | |
337 | + local msg = (tag == "iq") and "namespace" or "payload namespace"; | |
338 | + if not handlers then | |
339 | + stanza_handlers:add(module.host, origin_type, tag, xmlns, handler); | |
340 | + handler_info[handler] = module; | |
341 | + handler_table:add(module.host, module.name, {module.host, origin_type, tag, xmlns}); | |
342 | + --module:log("debug", "I now handle tag '%s' [%s] with %s '%s'", tag, origin_type, msg, xmlns); | |
343 | + else | |
344 | + module:log("warn", "I wanted to handle tag '%s' [%s] with %s '%s' but mod_%s already handles that", tag, origin_type, msg, xmlns, handler_info[handlers[1]].module.name); | |
345 | + end | |
346 | +end | |
347 | + | |
348 | +function api:add_handler(origin_type, tag, xmlns, handler) | |
349 | + if not (origin_type and tag and xmlns and handler) then return false; end | |
350 | + if type(origin_type) == "table" then | |
351 | + for _, origin_type in ipairs(origin_type) do | |
352 | + _add_handler(self, origin_type, tag, xmlns, handler); | |
353 | + end | |
354 | + else | |
355 | + _add_handler(self, origin_type, tag, xmlns, handler); | |
356 | + end | |
357 | +end | |
358 | +function api:add_iq_handler(origin_type, xmlns, handler) | |
359 | + self:add_handler(origin_type, "iq", xmlns, handler); | |
360 | +end | |
361 | + | |
362 | +function api:add_feature(xmlns) | |
363 | + self:add_item("feature", xmlns); | |
364 | +end | |
365 | +function api:add_identity(category, type, name) | |
366 | + self:add_item("identity", {category = category, type = type, name = name}); | |
367 | +end | |
368 | + | |
369 | +local event_hook = function(host, mod_name, event_name, ...) | |
370 | + if type((...)) == "table" and (...).host and (...).host ~= host then return; end | |
371 | + for handler in pairs(event_hooks:get(host, mod_name, event_name) or NULL) do | |
372 | + handler(...); | |
373 | + end | |
374 | +end; | |
375 | +function api:add_event_hook(name, handler) | |
376 | + if not hooked:get(self.host, self.name, name) then | |
377 | + eventmanager.add_event_hook(name, function(...) event_hook(self.host, self.name, name, ...); end); | |
378 | + hooked:set(self.host, self.name, name, true); | |
379 | + end | |
380 | + event_hooks:set(self.host, self.name, name, handler, true); | |
381 | +end | |
382 | + | |
383 | +function api:fire_event(...) | |
384 | + return (hosts[self.host] or prosody).events.fire_event(...); | |
385 | +end | |
386 | + | |
387 | +function api:hook(event, handler, priority) | |
388 | + hooks:set(self.host, self.name, event, handler, true); | |
389 | + (hosts[self.host] or prosody).events.add_handler(event, handler, priority); | |
390 | +end | |
391 | + | |
392 | +function api:hook_stanza(xmlns, name, handler, priority) | |
393 | + if not handler and type(name) == "function" then | |
394 | + -- If only 2 options then they specified no xmlns | |
395 | + xmlns, name, handler, priority = nil, xmlns, name, handler; | |
396 | + elseif not (handler and name) then | |
397 | + self:log("warn", "Error: Insufficient parameters to module:hook_stanza()"); | |
398 | + return; | |
399 | + end | |
400 | + return api.hook(self, "stanza/"..(xmlns and (xmlns..":") or "")..name, function (data) return handler(data.origin, data.stanza, data); end, priority); | |
401 | +end | |
402 | + | |
403 | +function api:require(lib) | |
404 | + local f, n = pluginloader.load_code(self.name, lib..".lib.lua"); | |
405 | + if not f then | |
406 | + f, n = pluginloader.load_code(lib, lib..".lib.lua"); | |
407 | + end | |
408 | + if not f then error("Failed to load plugin library '"..lib.."', error: "..n); end -- FIXME better error message | |
409 | + setfenv(f, self.environment); | |
410 | + return f(); | |
411 | +end | |
412 | + | |
413 | +function api:get_option(name, default_value) | |
414 | + local value = config.get(self.host, self.name, name); | |
415 | + if value == nil then | |
416 | + value = config.get(self.host, "core", name); | |
417 | + if value == nil then | |
418 | + value = default_value; | |
419 | + end | |
420 | + end | |
421 | + return value; | |
422 | +end | |
423 | + | |
424 | +function api:get_option_string(name, default_value) | |
425 | + local value = self:get_option(name, default_value); | |
426 | + if type(value) == "table" then | |
427 | + if #value > 1 then | |
428 | + self:log("error", "Config option '%s' does not take a list, using just the first item", name); | |
429 | + end | |
430 | + value = value[ |