summaryrefslogtreecommitdiff
path: root/xmpp/pylon.lua
blob: 9568999ce1bcd94f9e013beb126dac3b56bcaed5 (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
local socket = require'cqueues.socket'
local class = require'r.class'

local Channel = require'channel'
local BasePylon = require 'pylon'
local xml = require'xmpp.xml'
local X,xmlify = xml.X, xml.xmlify
local base64 = require'xmpp.base64'
local sha1 = require'xmpp.sha1'

local Xmpp = class.extend(BasePylon)

function Xmpp.init(self)
	self:_check_fields"server component component_secret"
	self.nicks_inuse = {}
end

-- only use on local connections!
function Xmpp._connect_component(self)
	local sock = assert(socket.connect(self.server, 5347))
	self.sock = sock
	sock:setmode('bn','bn')
	-- yes, our component name goes in the 'to' field. don't ask me why
	local start = ([[<stream:stream to='%s' xmlns='jabber:component:accept' xmlns:stream='http://etherx.jabber.org/streams'>]]):format(self.component)

	-- state of the art xml parser
	local function check_and_send(test, text)
		local x = sock:read('-2048')
		assert(x:find(test))
		if text then sock:write(text) end
	end

	sock:write(start)
	local streamhead = sock:read('-2048')
	assert(streamhead:find'accept')
	local streamid = streamhead:match"id='(.-)'"
	sock:write(xmlify(X.handshake{sha1.sha1(streamid..self.component_secret)}))
	check_and_send('<handshake/>',nil)

	return sock
end
Xmpp._connect = Xmpp._connect_component

function Xmpp.recving(self)
	local function getmore()
		local function t(...)
			-- pprint(...)
			return ...
		end
		return t(self.sock:read'-2048')
	end
	for x in xml.stanzae(getmore) do
		self.log:debug(xmlify(x))
		local body = x'body' and x'body'[1]
		if x.label == 'message' then
			local from = x.xarg.from
			local to = x.xarg.to
			local from_addr, from_nick = from and from:match"(.*)/(.*)"
			if
				from_nick
				and not self.nicks_inuse[from_nick]
				and body
				and to == 'wilson@'..self.component
			then
				self.log:proto('<',from_addr,from_nick,body)
				self.wilson:deliver(Channel(self, from_addr), {
					body = body,
					sender = '[x]'..from_nick
				})
			end
		end
	end
end

function Xmpp.sending(self)
	local users_inuse = {}
	local function ensure_joined(muc,user,nick)
		if self.nicks_inuse[nick] then return end
		user = user:gsub("[^a-zA-Z0-9%.]","."):match("^%.*(.-)%.*$")
		while users_inuse[user] do user = user..'-' end
		local jid = user..'@'..self.component
		local mucjid = muc..'/'..nick
		self.nicks_inuse[nick] = true
		users_inuse[user] = true

		self.sock:write(xmlify(
			X.presence{from=jid, to=mucjid,
				X.x{xmlns='http://jabber.org/protocol/muc',
					X.history{maxstanzas='0'}}}))
	end
	ensure_joined('d@conference.ubq323.website', 'wilson', 'wilson')
	for dest_channel, message in self.inbox:iter() do
		self.log:proto('>',dest_channel,message.sender,message.body)
		local muc = dest_channel.descriptor
		ensure_joined(muc, message.sender, message.sender)
		local user = message.sender:gsub("[^a-zA-Z0-9%.]","."):match("^%.*(.-)%.*$")
		local jid = user..'@'..self.component
		self.sock:write(xmlify(
			X.message{to=muc, type='groupchat', from=jid,
				X.body{message.body}}))
	end
end

return Xmpp