+ 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;
+
+ if opt_keepalives then
+ conn:setoption("keepalive", opt_keepalives);
+ end
+
+ session.log("info", "Incoming Jabber component connection");
+
+ local stream = new_xmpp_stream(session, stream_callbacks);
+ session.stream = stream;
+
+ session.notopen = true;
+
+ function session.reset_stream()
+ session.notopen = true;
+ session.stream:reset();
+ end
+
+ function session.data(conn, data)
+ local ok, err = stream:feed(data);
+ if ok then return; end
+ module:log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_"));
+ session:close("not-well-formed");
+ end
+
+ session.dispatch_stanza = stream_callbacks.handlestanza;
+
+ sessions[conn] = session;
+end
+function listener.onincoming(conn, data)
+ local session = sessions[conn];
+ session.data(conn, data);
+end
+function listener.ondisconnect(conn, err)
+ local session = sessions[conn];
+ if session then
+ (session.log or log)("info", "component disconnected: %s (%s)", tostring(session.host), tostring(err));
+ if session.on_destroy then session:on_destroy(err); end
+ sessions[conn] = nil;
+ for k in pairs(session) do
+ if k ~= "log" and k ~= "close" then
+ session[k] = nil;
+ end
+ end
+ session.destroyed = true;
+ session = nil;