+
+ -- Handle authentication attempts by component
+ local function handle_component_auth(event)
+ local session, stanza = event.origin, event.stanza;
+
+ if session.type ~= "component_unauthed" then return; end
+
+ if (not session.host) or #stanza.tags > 0 then
+ (session.log or log)("warn", "Invalid component handshake for host: %s", session.host);
+ session:close("not-authorized");
+ return true;
+ end
+
+ local secret = module:get_option("component_secret");
+ if not secret then
+ (session.log or log)("warn", "Component attempted to identify as %s, but component_secret is not set", session.host);
+ session:close("not-authorized");
+ return true;
+ end
+
+ local supplied_token = t_concat(stanza);
+ local calculated_token = sha1(session.streamid..secret, true);
+ if supplied_token:lower() ~= calculated_token:lower() then
+ module:log("info", "Component authentication failed for %s", session.host);
+ session:close{ condition = "not-authorized", text = "Given token does not match calculated token" };
+ return true;
+ end
+
+ if env.connected then
+ module:log("error", "Second component attempted to connect, denying connection");
+ session:close{ condition = "conflict", text = "Component already connected" };
+ return true;
+ end
+
+ env.connected = true;
+ send = session.send;
+ session.on_destroy = on_destroy;
+ session.component_validate_from = module:get_option_boolean("validate_from_addresses", true);
+ session.type = "component";
+ module:log("info", "External component successfully authenticated");
+ session.send(st.stanza("handshake"));
+
+ return true;
+ end
+ module:hook("stanza/jabber:component:accept:handshake", handle_component_auth);
+
+ -- Handle stanzas addressed to this component
+ local function handle_stanza(event)
+ local stanza = event.stanza;
+ if send then
+ stanza.attr.xmlns = nil;
+ send(stanza);
+ else
+ if stanza.name == "iq" and stanza.attr.type == "get" and stanza.attr.to == module.host then
+ local query = stanza.tags[1];
+ local node = query.attr.node;
+ if query.name == "query" and query.attr.xmlns == "http://jabber.org/protocol/disco#info" and (not node or node == "") then
+ local name = module:get_option_string("name");
+ if name then
+ event.origin.send(st.reply(stanza):tag("query", { xmlns = "http://jabber.org/protocol/disco#info" })
+ :tag("identity", { category = "component", type = "generic", name = module:get_option_string("name", "Prosody") }))
+ return true;
+ end
+ end
+ end
+ module:log("warn", "Component not connected, bouncing error for: %s", stanza:top_tag());
+ if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
+ event.origin.send(st.error_reply(stanza, "wait", "service-unavailable", "Component unavailable"));
+ end
+ end
+ return true;
+ end
+
+ module:hook("iq/bare", handle_stanza, -1);
+ module:hook("message/bare", handle_stanza, -1);
+ module:hook("presence/bare", handle_stanza, -1);
+ module:hook("iq/full", handle_stanza, -1);
+ module:hook("message/full", handle_stanza, -1);
+ module:hook("presence/full", handle_stanza, -1);
+ module:hook("iq/host", handle_stanza, -1);
+ module:hook("message/host", handle_stanza, -1);
+ module:hook("presence/host", handle_stanza, -1);