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.mucs = {} end -- only use on local connections! function Xmpp._connect(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 = ([[]]):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('',nil) for busname, bus in pairs(self.wilson.busses) do for _, channel in ipairs(bus) do if channel.pylon == self then self:add_channel(channel) end end end return sock end function Xmpp.add_channel(self, channel) local muc = { nick_to_user={}, user_to_nick={} } self.mucs[channel.descriptor] = muc self.nick_bot = self:ensure_and_get_user(channel.descriptor, "wilson") end function Xmpp.ensure_and_get_user(self, muc_jid, nick) local muc = assert(self.mucs[muc_jid]) local user = muc.nick_to_user[nick] if user then local user_jid = user..'@'..self.component local nick_jid = muc_jid..'/'..nick return user, nick, user_jid, nick_jid end -- join up new user user = nick:gsub("[^a-zA-Z0-9%.]","."):match("^%.*(.-)%.*$") while muc.user_to_nick[user] do user = user..'-' end muc.nick_to_user[nick] = user muc.user_to_nick[user] = nick local user_jid = user..'@'..self.component local nick_jid = muc_jid..'/'..nick self.log("Joining up new user "..user_jid.." to "..nick_jid) self.sock:write(xmlify( X.presence{from=user_jid, to=nick_jid, X.x{xmlns='http://jabber.org/protocol/muc', X.history{maxstanzas='0'}}})) return user, nick, user_jid, nick_jid end 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_bare_jid, from_nick if x.xarg.from then from_bare_jid, from_nick = x.xarg.from:match"(.*)/(.*)" end local muc = self.mucs[from_bare_jid] ; if muc then if x.xarg.to == 'wilson@'..self.component and body and from_nick and not muc.nick_to_user[from_nick] then self.log:proto('<',from_bare_jid,from_nick,body) self.wilson:deliver(Channel(self, from_bare_jid), { body = body, sender = '[x]'..from_nick }) end end end end end function Xmpp.sending(self) for dest_channel, message in self.inbox:iter() do self.log:proto('>',dest_channel,message.sender,message.body) local user, nick, user_jid, nick_jid = self:ensure_and_get_user(dest_channel.descriptor, message.sender) self.sock:write(xmlify( X.message{to=dest_channel.descriptor, type='groupchat', from=user_jid, X.body{message.body}})) end end return Xmpp