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
|
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
local THE_MUC = 'd@conference.ubq323.website'
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
-- pprint(x)
-- print(xmlify(x))
local body = x'body' and x'body'[1]
if x.label == 'message' then
local fr = x.xarg.from
local t = x.xarg.to
local from_nick = fr:match("/(.*)")
if not self.nicks_inuse[from_nick] and body and fr:match"/" and t == 'wilson@'..self.component then
self.wilson:deliver(Channel(self, THE_MUC), {
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(THE_MUC, 'wilson', 'wilson')
for dest_channel, message in self.inbox:iter() do
-- pprint(dest_channel, message)
ensure_joined(THE_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=THE_MUC, type='groupchat', from=jid,
X.body{message.body}}))
end
end
return Xmpp
|