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
|
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 = ([[<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)
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
local bot = {} ; self.bot = bot
bot.user, bot.nick, bot.user_jid, bot.nick_jid = 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
::next::
self.log:debug(xmlify(x))
if type(x) == 'string' then
self.log:warn("x was a string: ("..x..")")
goto next
end
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
|