1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
-- Prosody IM
-- Copyright (C) 2008-2009 Matthew Wild
-- Copyright (C) 2008-2009 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
local prosody = _G.prosody;
local log = require "util.logger".init("componentmanager");
local configmanager = require "core.configmanager";
local modulemanager = require "core.modulemanager";
local jid_split = require "util.jid".split;
local fire_event = require "core.eventmanager".fire_event;
local events_new = require "util.events".new;
local st = require "util.stanza";
local prosody, hosts = prosody, prosody.hosts;
local ssl = ssl;
local uuid_gen = require "util.uuid".generate;
local pairs, setmetatable, type, tostring = pairs, setmetatable, type, tostring;
local components = {};
local disco_items = require "util.multitable".new();
local NULL = {};
module "componentmanager"
local function default_component_handler(origin, stanza)
log("warn", "Stanza being handled by default component; bouncing error for: %s", stanza:top_tag());
if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
origin.send(st.error_reply(stanza, "wait", "service-unavailable", "Component unavailable"));
end
end
function load_enabled_components(config)
local defined_hosts = config or configmanager.getconfig();
for host, host_config in pairs(defined_hosts) do
if host ~= "*" and ((host_config.core.enabled == nil or host_config.core.enabled) and type(host_config.core.component_module) == "string") then
hosts[host] = create_component(host);
hosts[host].connected = false;
components[host] = default_component_handler;
local ok, err = modulemanager.load(host, host_config.core.component_module);
if not ok then
log("error", "Error loading %s component %s: %s", tostring(host_config.core.component_module), tostring(host), tostring(err));
else
fire_event("component-activated", host, host_config);
log("debug", "Activated %s component: %s", host_config.core.component_module, host);
end
end
end
end
if prosody and prosody.events then
prosody.events.add_handler("server-starting", load_enabled_components);
end
function handle_stanza(origin, stanza)
local node, host = jid_split(stanza.attr.to);
local component = nil;
if host then
if node then component = components[node.."@"..host]; end -- hack to allow hooking node@server
if not component then component = components[host]; end
end
if component then
log("debug", "%s stanza being handled by component: %s", stanza.name, host);
component(origin, stanza, hosts[host]);
else
log("error", "Component manager recieved a stanza for a non-existing component: "..tostring(stanza));
default_component_handler(origin, stanza);
end
end
function create_component(host, component, events)
-- TODO check for host well-formedness
local ssl_ctx, ssl_ctx_in;
if host and ssl then
-- We need to find SSL context to use...
-- Discussion in prosody@ concluded that
-- 1 level back is usually enough by default
local base_host = host:gsub("^[^%.]+%.", "");
if hosts[base_host] then
ssl_ctx = hosts[base_host].ssl_ctx;
ssl_ctx_in = hosts[base_host].ssl_ctx_in;
elseif prosody.global_ssl_ctx then
-- We have no cert, and no parent host to borrow a cert from
-- Use global/default cert if there is one
ssl_ctx = ssl.newcontext(prosody.global_ssl_ctx);
ssl_ctx_in = ssl.newcontext(setmetatable({ mode = "server" }, { __index = prosody.global_ssl_ctx }));
end
end
return { type = "component", host = host, connected = true, s2sout = {},
ssl_ctx = ssl_ctx, ssl_ctx_in = ssl_ctx_in, events = events or events_new(),
dialback_secret = configmanager.get(host, "core", "dialback_secret") or uuid_gen() };
end
function register_component(host, component, session)
if not hosts[host] or (hosts[host].type == 'component' and not hosts[host].connected) then
local old_events = hosts[host] and hosts[host].events;
components[host] = component;
hosts[host] = session or create_component(host, component, old_events);
-- Add events object if not already one
if not hosts[host].events then
hosts[host].events = old_events or events_new();
end
if not hosts[host].dialback_secret then
hosts[host].dialback_secret = configmanager.get(host, "core", "dialback_secret") or uuid_gen();
end
-- add to disco_items
if not(host:find("@", 1, true) or host:find("/", 1, true)) and host:find(".", 1, true) then
disco_items:set(host:sub(host:find(".", 1, true)+1), host, true);
end
modulemanager.load(host, "dialback");
modulemanager.load(host, "tls");
log("debug", "component added: "..host);
return session or hosts[host];
else
log("error", "Attempt to set component for existing host: "..host);
end
end
function deregister_component(host)
if components[host] then
modulemanager.unload(host, "tls");
modulemanager.unload(host, "dialback");
hosts[host].connected = nil;
local host_config = configmanager.getconfig()[host];
if host_config and ((host_config.core.enabled == nil or host_config.core.enabled) and type(host_config.core.component_module) == "string") then
-- Set default handler
components[host] = default_component_handler;
else
-- Component not in config, or disabled, remove
hosts[host] = nil; -- FIXME do proper unload of all modules and other cleanup before removing
components[host] = nil;
end
-- remove from disco_items
if not(host:find("@", 1, true) or host:find("/", 1, true)) and host:find(".", 1, true) then
disco_items:remove(host:sub(host:find(".", 1, true)+1), host);
end
log("debug", "component removed: "..host);
return true;
else
log("error", "Attempt to remove component for non-existing host: "..host);
end
end
function set_component_handler(host, handler)
components[host] = handler;
end
function get_children(host)
return disco_items:get(host) or NULL;
end
return _M;
|