+ session.host = attr.to;
+ session.streamid = uuid_gen();
+ session.notopen = nil;
+ -- Return stream header
+ session.send("<?xml version='1.0'?>");
+ session.send(st.stanza("stream:stream", { xmlns=xmlns_component,
+ ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.host }):top_tag());
+end
+
+function stream_callbacks.streamclosed(session)
+ session.log("debug", "Received </stream:stream>");
+ session:close();
+end
+
+local function handleerr(err) log("error", "Traceback[component]: %s", traceback(tostring(err), 2)); end
+function stream_callbacks.handlestanza(session, stanza)
+ -- Namespaces are icky.
+ if not stanza.attr.xmlns and stanza.name == "handshake" then
+ stanza.attr.xmlns = xmlns_component;
+ end
+ if not stanza.attr.xmlns or stanza.attr.xmlns == "jabber:client" then
+ local from = stanza.attr.from;
+ if from then
+ if session.component_validate_from then
+ local _, domain = jid_split(stanza.attr.from);
+ if domain ~= session.host then
+ -- Return error
+ session.log("warn", "Component sent stanza with missing or invalid 'from' address");
+ session:close{
+ condition = "invalid-from";
+ text = "Component tried to send from address <"..tostring(from)
+ .."> which is not in domain <"..tostring(session.host)..">";
+ };
+ return;
+ end
+ end
+ else
+ stanza.attr.from = session.host; -- COMPAT: Strictly we shouldn't allow this
+ end
+ if not stanza.attr.to then
+ session.log("warn", "Rejecting stanza with no 'to' address");
+ session.send(st.error_reply(stanza, "modify", "bad-request", "Components MUST specify a 'to' address on stanzas"));
+ return;
+ end
+ end
+
+ if stanza then
+ return xpcall(function () return core_process_stanza(session, stanza) end, handleerr);
+ end
+end
+
+--- Closing a component connection
+local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'};
+local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" };
+local function session_close(session, reason)
+ if session.destroyed then return; end
+ if session.conn then
+ if session.notopen then
+ session.send("<?xml version='1.0'?>");
+ session.send(st.stanza("stream:stream", default_stream_attr):top_tag());
+ end
+ if reason then
+ if type(reason) == "string" then -- assume stream error
+ module:log("info", "Disconnecting component, <stream:error> is: %s", reason);
+ session.send(st.stanza("stream:error"):tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' }));
+ elseif type(reason) == "table" then
+ if reason.condition then
+ local stanza = st.stanza("stream:error"):tag(reason.condition, stream_xmlns_attr):up();
+ if reason.text then
+ stanza:tag("text", stream_xmlns_attr):text(reason.text):up();
+ end
+ if reason.extra then
+ stanza:add_child(reason.extra);
+ end
+ module:log("info", "Disconnecting component, <stream:error> is: %s", tostring(stanza));
+ session.send(stanza);
+ elseif reason.name then -- a stanza
+ module:log("info", "Disconnecting component, <stream:error> is: %s", tostring(reason));
+ session.send(reason);
+ end
+ end
+ end
+ session.send("</stream:stream>");
+ session.conn:close();
+ listener.ondisconnect(session.conn, "stream error");
+ end
+end
+
+--- Component connlistener
+
+function listener.onconnect(conn)
+ local _send = conn.write;
+ local session = { type = "component_unauthed", conn = conn, send = function (data) return _send(conn, tostring(data)); end };
+
+ -- Logging functions --
+ local conn_name = "jcp"..tostring(session):match("[a-f0-9]+$");
+ session.log = logger.init(conn_name);
+ session.close = session_close;