aboutsummaryrefslogtreecommitdiffstats
path: root/core/stanza_dispatch.lua
blob: e76d6dddcc390fb9091c145747b9c26ebb1eb0a5 (plain)
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

require "util.stanza"

local st = stanza;

local t_concat = table.concat;
local format = string.format;

function init_stanza_dispatcher(session)
	local iq_handlers = {};

	local session_log = session.log;
	local log = function (type, msg) session_log(type, "stanza_dispatcher", msg); end
	local send = session.send;
	local send_to;
	do
		local _send_to = session.send_to;
		send_to = function (...) _send_to(session, ...); end
	end

	iq_handlers["jabber:iq:auth"] = 
		function (stanza)
			local username = stanza.tags[1]:child_with_name("username");
			local password = stanza.tags[1]:child_with_name("password");
			local resource = stanza.tags[1]:child_with_name("resource");
			if not (username and password and resource) then
				local reply = st.reply(stanza);
				send(reply:query("jabber:iq:auth")
					:tag("username"):up()
					:tag("password"):up()
					:tag("resource"):up());
				return true;			
			else
				username, password, resource = t_concat(username), t_concat(password), t_concat(resource);
				local reply = st.reply(stanza);
				require "core.usermanager"
				if usermanager.validate_credentials(session.host, username, password) then
					-- Authentication successful!
					session.username = username;
					session.resource = resource;
					session.full_jid = username.."@"..session.host.."/"..session.resource;
					if not hosts[session.host].sessions[username] then
						hosts[session.host].sessions[username] = { sessions = {} };
					end
					hosts[session.host].sessions[username].sessions[resource] = session;
					send(st.reply(stanza));
					return true;
				else
					local reply = st.reply(stanza);
					reply.attr.type = "error";
					reply:tag("error", { code = "401", type = "auth" })
						:tag("not-authorized", { xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas" });
					send(reply);
					return true;
				end
			end
			
		end
		
	iq_handlers["jabber:iq:roster"] =
		function (stanza)
			if stanza.attr.type == "get" then
				session.roster = session.roster or rostermanager.getroster(session.username, session.host);
				if session.roster == false then
					send(st.reply(stanza)
						:tag("error", { type = "wait" })
						:tag("internal-server-error", { xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}));
					return true;
				else session.roster = session.roster or {};
				end
				local roster = st.reply(stanza)
							:query("jabber:iq:roster");
				for jid in pairs(session.roster) do
					roster:tag("item", { jid = jid, subscription = "none" }):up();
				end
				send(roster);
				return true;
			end
		end


	return	function (stanza)
			log("info", "--> "..tostring(stanza));
			if (not stanza.attr.to) or (hosts[stanza.attr.to] and hosts[stanza.attr.to].type == "local") then
				if stanza.name == "iq" then
					if not stanza.tags[1] then log("warn", "<iq> without child is invalid"); return; end
					if not stanza.attr.id then log("warn", "<iq> without id attribute is invalid"); end
					local xmlns = (stanza.tags[1].attr and stanza.tags[1].attr.xmlns) or nil;
					if not xmlns then log("warn", "Child of <iq> has no xmlns - invalid"); return; end
					if (((not stanza.attr.to) or stanza.attr.to == session.host or stanza.attr.to:match("@[^/]+$")) and (stanza.attr.type == "get" or stanza.attr.type == "set")) then -- Stanza sent to us
						if iq_handlers[xmlns] then
							if iq_handlers[xmlns](stanza) then return; end;
						end
						log("warn", "Unhandled namespace: "..xmlns);
						send(format("<iq type='error' id='%s'><error type='cancel'><service-unavailable/></error></iq>", stanza.attr.id));
						return;
					end
				end
				if not session.username then log("warn", "Attempt to use an unauthed stream!"); return; end
				if stanza.name == "presence" then
					if session.roster then
						local initial_presence = not session.last_presence;
						session.last_presence = stanza;
						
						-- Broadcast presence and probes
						local broadcast = st.presence({ from = session.full_jid, type = stanza.attr.type });
						--local probe = st.presence { from = broadcast.attr.from, type = "probe" };

						for child in stanza:childtags() do
							broadcast:add_child(child);
						end
						for contact_jid in pairs(session.roster) do
							broadcast.attr.to = contact_jid;
							send_to(contact_jid, broadcast);
							if initial_presence then
								local node, host = jid.split(contact_jid);
								if hosts[host] and hosts[host].type == "local" then
									local contact = hosts[host].sessions[node]
									if contact then
										local pres = st.presence { to = session.full_jid };
										for resource, contact_session in pairs(contact.sessions) do
											if contact_session.last_presence then
												pres.tags = contact_session.last_presence.tags;
												pres.attr.from = contact_session.full_jid;
												send(pres);
											end
										end
									end
									--FIXME: Do we send unavailable if they are offline?
								else
									probe.attr.to = contact;
									send_to(contact, probe);
								end
							end
						end
						
						-- Probe for our contacts' presence
					end
				end
			elseif session.username then
			--end				
			--if stanza.attr.to and ((not hosts[stanza.attr.to]) or hosts[stanza.attr.to].type ~= "local") then
				-- Need to route stanza
				stanza.attr.from = session.username.."@"..session.host;
				session:send_to(stanza.attr.to, stanza);
			end
		end

end