summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--base64.lua38
-rw-r--r--irc.lua138
-rw-r--r--main.lua88
-rw-r--r--queue.lua8
-rw-r--r--rirc.lua2
-rw-r--r--sha1.lua169
-rw-r--r--test.lua24
-rw-r--r--wilson.ini33
-rw-r--r--xml.lua34
-rw-r--r--xml_old.lua48
-rw-r--r--xmpp.lua180
11 files changed, 604 insertions, 158 deletions
diff --git a/base64.lua b/base64.lua
new file mode 100644
index 0000000..4f9a967
--- /dev/null
+++ b/base64.lua
@@ -0,0 +1,38 @@
+local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+
+-- really bad
+local function encode(s)
+ local x = 0
+ local n = 0
+ local out = {}
+ local outn = 1
+ local pad = ""
+ local function si(v, m) x = (x << m) | v; n = n + m end
+ local function so(m)
+ local rem = n - m
+ local res = x >> rem
+ x = x & ((1 << rem) - 1)
+ n = n - m
+ return res
+ end
+ local function o()
+ while n >= 6 do
+ local i = so(6)+1
+ out[outn] = alphabet:sub(i,i)
+ outn = outn + 1
+ end
+ end
+ for i = 1, #s do
+ si(s:byte(i), 8)
+ o()
+ end
+ if n > 0 then
+ local on = n
+ si(0, 6-n)
+ o()
+ pad = ('='):rep(3-on/2) -- bad
+ end
+ return table.concat(out)..pad
+end
+
+return {encode=encode}
diff --git a/irc.lua b/irc.lua
index d6f8dae..541c725 100644
--- a/irc.lua
+++ b/irc.lua
@@ -1,13 +1,24 @@
local cqueues = require'cqueues'
local socket = require'cqueues.socket'
-local condition = require'cqueues.condition'
local pprint = require'pprint'
local rirc = require'rirc'
+local Queue = require'queue'
local Irc = {}
-function Irc.makepylon(pylonname, conf)
- local self = {pylonname=pylonname}
+function Irc._send(self, args)
+ args.source = args.source or self.nodename
+ local sent = rirc.send(self.sock, args)
+ print('>', sent)
+end
+
+function Irc.makepylon(pylonname, conf, cq, network)
+ local self = {
+ pylonname=pylonname,
+ cq = cq,
+ network = network,
+ inbox = Queue.make(),
+ }
local function conf_var(name)
assert(conf[name] ~= nil, 'missing conf field '..name)
self[name] = conf[name]
@@ -17,21 +28,20 @@ function Irc.makepylon(pylonname, conf)
conf_var 'password'
conf_var 'nodename'
- self.outqueue = {}
- self.outcv = condition.new()
setmetatable(self, {__index=Irc})
return self
end
-function Irc.send(self, args)
- args.source = args.source or self.nodename
- rirc.send(self.sock, args)
+function Irc._connect(self)
+ self.sock = assert(socket.connect(self.host, self.port))
+ self:_send{'PASS', self.password, '0210-IRC', 'wilson|'}
+ self:_send{'SERVER', self.nodename, '1', 'i am wilson'}
end
-function Irc.connect(self)
- self.sock = assert(socket.connect(self.host, self.port))
- self:send{'PASS', self.password, '0210-IRC', 'wilson|'}
- self:send{'SERVER', self.nodename, '1', 'i am wilson'}
+function Irc.run(self)
+ self:_connect()
+ self.cq:wrap(self.recving, self)
+ self.cq:wrap(self.sending, self)
end
function Irc.recving(self)
@@ -39,20 +49,15 @@ function Irc.recving(self)
print('<', line)
local msg = rirc.parse(line)
if msg.op == 'PING' then
- self:send{'PONG', msg.args[1]}
+ self:_send{'PONG', msg.args[1]}
elseif msg.op == 'PRIVMSG' then
- local ch = msg.args[1]
+ local channel = msg.args[1]
local body = msg.args[2]
local source = msg.source
- local neighbors = self.network:neighbors_of(self.pylonname, ch)
- pprint('gt neighbors', neighbors)
- for _,other in ipairs(neighbors) do
- other.pylon:enqueue {
- channel = other.channel,
- body = body,
- source = source..'[i]'
- }
- end
+ self.network:post(self.pylonname, channel, {
+ body = body,
+ source = source..'[i]'
+ })
end
end
end
@@ -61,69 +66,54 @@ function Irc.sending(self)
local nicks_channels = {}
local function ensure_joined(nick, channel)
if not nicks_channels[nick] then
- self:send{'NICK', nick, 1, 'username', 'host', 1, '+', 'realname'}
+ self:_send{'NICK', nick, 1, 'username', 'host', 1, '+', 'realname'}
nicks_channels[nick] = {}
end
if not nicks_channels[nick][channel] then
- self:send{source=nick, 'JOIN', channel}
+ self:_send{source=nick, 'JOIN', channel}
nicks_channels[nick][channel] = true
end
end
local function say(nick, channel, body)
ensure_joined(nick, channel)
- self:send{source=nick, 'PRIVMSG', channel, body}
+ self:_send{source=nick, 'PRIVMSG', channel, body}
end
say('WILSON', '#test', 'i am wilson')
- while true do
- while #self.outqueue > 0 do
- local queue = self.outqueue
- self.outqueue = {}
- for _, outmsg in pairs(queue) do
- assert(say(outmsg.source, outmsg.channel, outmsg.body))
- end
- end
- self.outcv:wait()
+ for ch, msg in self.inbox:iter() do
+ say(msg.source, ch, msg.body)
end
end
-function Irc.enqueue(self, message)
- table.insert(self.outqueue, message)
- self.outcv:signal()
-end
-
-function Irc.run(self, cq, network)
- self.network = network
- self:connect()
- cq:wrap(self.recving, self)
- cq:wrap(self.sending, self)
-end
-
-
-local cq = cqueues.new()
-local conf = {
- host = 'localhost',
- port = '6667',
- password = 'mypassword',
- nodename = 'wilson.ubq323',
-}
-
-local dummy_pylon = {
- enqueue = function(self, msg)
- print(string.format(
- "%s <%s> | %s",
- msg.channel, msg.source, msg.body))
- end
-}
-
-local network = {neighbors_of = function(self, pylonname, channel)
- print('looking for neighbors of '..pylonname..', '..channel)
- return {
- {pylon = dummy_pylon, channel='#bridge'}
- }
-end}
-
-local pylon = Irc.makepylon('test', conf)
-pylon:run(cq, network)
-pprint(cq:loop())
+return Irc
+
+-- local cq = cqueues.new()
+-- local conf = {
+-- host = 'localhost',
+-- port = '6667',
+-- password = 'mypassword',
+-- nodename = 'wilson.ubq323',
+-- }
+
+-- local dummy_network = {
+-- post = function(self, pylonname, channel, message)
+-- pprint(pylonname, channel, message)
+-- end
+-- }
+
+-- local pylon = Irc.makepylon('test', conf, cq, dummy_network)
+-- pylon:run()
+-- cq:wrap(function()
+-- local i = 0
+-- while true do
+-- cqueues.sleep(1)
+-- pylon.inbox:enqueue{
+-- source = 'helen',
+-- channel = '#test',
+-- body = 'i am helen '..i,
+-- }
+-- i = i + 1
+-- end
+-- end)
+-- pprint('cheese', cq:loop())
diff --git a/main.lua b/main.lua
index cbd98d8..7d8c97d 100644
--- a/main.lua
+++ b/main.lua
@@ -1,34 +1,86 @@
-local irc=require'irc'
-local xmpp=require'xmpp'
-local discord=require'discord'
+local cqueues = require'cqueues'
+
+local pylon_types = {
+ irc = require'irc',
+ xmpp = require'xmpp',
+}
+
+local Network = {}
+function Network.make(pylon_confs, bus_confs)
+ local self = {
+ pylons={},
+ busses={},
+ cq = cqueues.new(),
+ }
+ for pylonname, conf in pairs(pylon_confs) do
+ local pty = pylon_types[conf.type]
+ self.pylons[pylonname] = pty.makepylon(pylonname, conf, self.cq, self)
+ print("constructed pylon",pylonname,conf.type)
+ end
+ for busname, conf in pairs(bus_confs) do
+ local bus = {}
+ for _, item in ipairs(conf) do
+ table.insert(bus, {pylonname=item[1], channel=item[2]})
+ end
+ self.busses[busname] = bus
+ end
+ return setmetatable(self, {__index=Network})
+end
+function Network._find_bus(self, pylonname, channel)
+ for name, bus in pairs(self.busses) do
+ for _, entry in ipairs(bus) do
+ if entry.pylonname == pylonname and entry.channel == channel then
+ return bus
+ end
+ end
+ end
+ print("warning: unfound bus for",pylonname,channel)
+end
+function Network.post(self, pylonname, channel, message)
+ local bus = self:_find_bus(pylonname, channel)
+ if bus then
+ for _, dest in ipairs(bus) do
+ if not (dest.pylonname == pylonname and dest.channel == channel) then
+ local target_pylon = self.pylons[dest.pylonname]
+ target_pylon.inbox:enqueue(dest.channel, message)
+ end
+ end
+ end
+end
+function Network.run(self)
+ for pylonname, pylon in pairs(self.pylons) do
+ self.cq:wrap(pylon.run, pylon)
+ print("now running pylon", pylonname)
+ end
+ print(self.cq:loop())
+end
+
+
+
local pylon_confs = {
xmpp_ubq323 = {
type='xmpp',
jid='wilson@ubq323.website',
- server='ubq323.website',
- auth='...',
+ server='localhost',
+ component='wilson.ubq323.website',
+ component_secret='super_secret_wilson_password',
resource='wilson',
},
- irc_apionet = {
+ irc_local = {
+ type='irc',
host='localhost',
- port=6667
+ port=6667,
password='mypassword',
nodename='wilson.ubq323',
},
}
-
-local pylons = {}
-for name, conf in pairs(pylon_confs) do
- pylons[name] = pylon_types[conf.type].make_pylon(name, conf)
-end
-
-
-local channels = {
+local bus_confs = {
d = {
- {xmpp_ubq323, 'd@conference.ubq323.website'},
- {irc_apionet, '#d'},
+ {'xmpp_ubq323', 'd@conference.ubq323.website'},
+ {'irc_local', '#test'},
},
}
-
+local the_network = Network.make(pylon_confs, bus_confs)
+the_network:run()
diff --git a/queue.lua b/queue.lua
index 303728e..8c9c373 100644
--- a/queue.lua
+++ b/queue.lua
@@ -11,8 +11,12 @@ function Queue.make()
}, {__index=Queue})
end
-function Queue.enqueue(self, item)
+function Queue.enqueue(self, ...)
+ local item = table.pack(...)
table.insert(self.items, item)
+ if #self.items > 128 then
+ print('warning: queue is quite big')
+ end
self.cv:signal()
end
@@ -23,7 +27,7 @@ function Queue.iter(self)
local items = self.items
self.items = {} -- the old switcheroo
for _, item in ipairs(items) do
- coroutine.yield(item)
+ coroutine.yield(table.unpack(item, 1, item.n))
end
end
self.cv:wait()
diff --git a/rirc.lua b/rirc.lua
index b341bfd..95445ad 100644
--- a/rirc.lua
+++ b/rirc.lua
@@ -18,8 +18,8 @@ function irc.send(sock, args)
end
local last = tostring(args[#args]):gsub("[\r\n]+"," ")
out = out .. ':' .. last
- print('>', out)
sock:write(out..'\r\n')
+ return out
end
function irc.parse(line)
diff --git a/sha1.lua b/sha1.lua
new file mode 100644
index 0000000..7451595
--- /dev/null
+++ b/sha1.lua
@@ -0,0 +1,169 @@
+-- from https://github.com/mpeterv/sha1
+-- by Enrique GarcĂ­a Cota, Eike Decker, Jeffrey Friedl, Peter Melnichenko
+-- (MIT license)
+
+local sha1 = {}
+
+-- Merges four bytes into a uint32 number.
+local function bytes_to_uint32(a, b, c, d)
+ return a * 0x1000000 + b * 0x10000 + c * 0x100 + d
+end
+
+-- Splits a uint32 number into four bytes.
+local function uint32_to_bytes(a)
+ local a4 = a % 256
+ a = (a - a4) / 256
+ local a3 = a % 256
+ a = (a - a3) / 256
+ local a2 = a % 256
+ local a1 = (a - a2) / 256
+ return a1, a2, a3, a4
+end
+
+local function uint32_lrot(a, bits)
+ return ((a << bits) & 0xFFFFFFFF) | (a >> (32 - bits))
+end
+
+local function uint32_ternary(a, b, c)
+ -- c ~ (a & (b ~ c)) has less bitwise operations than (a & b) | (~a & c).
+ return c ~ (a & (b ~ c))
+end
+
+local function uint32_majority(a, b, c)
+ -- (a & (b | c)) | (b & c) has less bitwise operations than (a & b) | (a & c) | (b & c).
+ return (a & (b | c)) | (b & c)
+end
+
+local sbyte = string.byte
+local schar = string.char
+local sformat = string.format
+local srep = string.rep
+
+local function hex_to_binary(hex)
+ return (hex:gsub("..", function(hexval)
+ return schar(tonumber(hexval, 16))
+ end))
+end
+
+-- Calculates SHA1 for a string, returns it encoded as 40 hexadecimal digits.
+function sha1.sha1(str)
+ -- Input preprocessing.
+ -- First, append a `1` bit and seven `0` bits.
+ local first_append = schar(0x80)
+
+ -- Next, append some zero bytes to make the length of the final message a multiple of 64.
+ -- Eight more bytes will be added next.
+ local non_zero_message_bytes = #str + 1 + 8
+ local second_append = srep(schar(0), -non_zero_message_bytes % 64)
+
+ -- Finally, append the length of the original message in bits as a 64-bit number.
+ -- Assume that it fits into the lower 32 bits.
+ local third_append = schar(0, 0, 0, 0, uint32_to_bytes(#str * 8))
+
+ str = str .. first_append .. second_append .. third_append
+ assert(#str % 64 == 0)
+
+ -- Initialize hash value.
+ local h0 = 0x67452301
+ local h1 = 0xEFCDAB89
+ local h2 = 0x98BADCFE
+ local h3 = 0x10325476
+ local h4 = 0xC3D2E1F0
+
+ local w = {}
+
+ -- Process the input in successive 64-byte chunks.
+ for chunk_start = 1, #str, 64 do
+ -- Load the chunk into W[0..15] as uint32 numbers.
+ local uint32_start = chunk_start
+
+ for i = 0, 15 do
+ w[i] = bytes_to_uint32(sbyte(str, uint32_start, uint32_start + 3))
+ uint32_start = uint32_start + 4
+ end
+
+ -- Extend the input vector.
+ for i = 16, 79 do
+ w[i] = uint32_lrot(w[i - 3] ~ w[i - 8] ~ w[i - 14] ~ w[i - 16], 1)
+ end
+
+ -- Initialize hash value for this chunk.
+ local a = h0
+ local b = h1
+ local c = h2
+ local d = h3
+ local e = h4
+
+ -- Main loop.
+ for i = 0, 79 do
+ local f
+ local k
+
+ if i <= 19 then
+ f = uint32_ternary(b, c, d)
+ k = 0x5A827999
+ elseif i <= 39 then
+ f = b ~ c ~ d
+ k = 0x6ED9EBA1
+ elseif i <= 59 then
+ f = uint32_majority(b, c, d)
+ k = 0x8F1BBCDC
+ else
+ f = b ~ c ~ d
+ k = 0xCA62C1D6
+ end
+
+ local temp = (uint32_lrot(a, 5) + f + e + k + w[i]) % 4294967296
+ e = d
+ d = c
+ c = uint32_lrot(b, 30)
+ b = a
+ a = temp
+ end
+
+ -- Add this chunk's hash to result so far.
+ h0 = (h0 + a) % 4294967296
+ h1 = (h1 + b) % 4294967296
+ h2 = (h2 + c) % 4294967296
+ h3 = (h3 + d) % 4294967296
+ h4 = (h4 + e) % 4294967296
+ end
+
+ return sformat("%08x%08x%08x%08x%08x", h0, h1, h2, h3, h4)
+end
+
+function sha1.binary(str)
+ return hex_to_binary(sha1.sha1(str))
+end
+
+-- Precalculate replacement tables.
+local xor_with_0x5c = {}
+local xor_with_0x36 = {}
+
+for i = 0, 0xff do
+ xor_with_0x5c[schar(i)] = schar(0x5c ~ i)
+ xor_with_0x36[schar(i)] = schar(0x36 ~ i)
+end
+
+-- 512 bits.
+local BLOCK_SIZE = 64
+
+function sha1.hmac(key, text)
+ if #key > BLOCK_SIZE then
+ key = sha1.binary(key)
+ end
+
+ local key_xord_with_0x36 = key:gsub('.', xor_with_0x36) .. srep(schar(0x36), BLOCK_SIZE - #key)
+ local key_xord_with_0x5c = key:gsub('.', xor_with_0x5c) .. srep(schar(0x5c), BLOCK_SIZE - #key)
+
+ return sha1.sha1(key_xord_with_0x5c .. sha1.binary(key_xord_with_0x36 .. text))
+end
+
+function sha1.hmac_binary(key, text)
+ return hex_to_binary(sha1.hmac(key, text))
+end
+
+setmetatable(sha1, {__call = function(_, str) return sha1.sha1(str) end})
+
+return sha1
+
diff --git a/test.lua b/test.lua
new file mode 100644
index 0000000..d70f7f2
--- /dev/null
+++ b/test.lua
@@ -0,0 +1,24 @@
+local cqueues = require'cqueues'
+local condition = require'cqueues.condition'
+
+local cv = condition.new()
+local function task1()
+ while true do
+ print('1 top')
+ cv:wait()
+ print('1 waited')
+ end
+end
+local function task2()
+ while true do
+ print('2 top')
+ cv:signal()
+ print('2 signalled')
+ cqueues.poll()
+ end
+end
+
+local cq = cqueues.new()
+cq:wrap(task1)
+cq:wrap(task2)
+print(cq:loop())
diff --git a/wilson.ini b/wilson.ini
new file mode 100644
index 0000000..b875b4f
--- /dev/null
+++ b/wilson.ini
@@ -0,0 +1,33 @@
+[pylon discord]
+type=discord
+token=abcdef123456
+
+[pylon apionet-irc]
+type=irc
+host=ubq323.website
+nick=wilson
+
+[pylon other-irc]
+type=irc
+host=someotherhost.example
+nick=gregory
+
+[pylon ubq-xmpp]
+type=xmpp
+jid=wilson@ubq323.website
+password=zyxw9876
+default-service=conference.ubq323.website
+
+
+[channel a]
+# apionet a
+discord 12345678 # esoserver #apionet
+discord 98765432 # apionet discord #a
+ubq-xmpp a@
+apionet-irc a
+
+[channel ja]
+discord 3141592654 # apionet discord #ja
+apionet-irc ja
+ubq-xmpp ja@
+
diff --git a/xml.lua b/xml.lua
index 35d2804..68fa741 100644
--- a/xml.lua
+++ b/xml.lua
@@ -2,6 +2,23 @@
-- modified by me a bit
+entity_escapes = {
+ ["<"]="&lt;",
+ [">"]="&gt;",
+ ["&"]="&amp;",
+ ['"']="&quot;",
+ ["'"]="&apos;"
+}
+entity_unescapes = {}
+for k,v in pairs(entity_escapes) do entity_unescapes[v]=k end
+
+function escape(s)
+ return s:gsub("[<>&'\"]",entity_escapes)
+end
+function unescape(s)
+ return s:gsub("&[a-z]+;",entity_unescapes)
+end
+
local function parseargs(s)
local arg = {}
string.gsub(s, "([%-%w]+)=([\"'])(.-)%2", function (w, _, a)
@@ -46,14 +63,14 @@ psingle = function(s, i)
elseif #rest == 0 then
error('empty string')
else
- return i+#rest, rest
+ return i+#rest, unescape(rest)
end
end
local nexti = j+1
local pretext = s:sub(i, ts-1)
if not pretext:find("^%s*$") then -- not entirely whitespace
- return ts, pretext
+ return ts, unescape(pretext)
end
if empty == "/" then
@@ -80,24 +97,13 @@ local function stanzae(getmore)
coroutine.yield(el)
buf = buf:sub(ni)
else
- local more = getmore()
+ local more = assert(getmore())
buf = buf .. more
end
end
end)
end
-local entity_escapes = {
- ["<"]="&lt;",
- [">"]="&gt;",
- ["&"]="&amp;",
- ['"']="&quot;",
- ["'"]="&apos;"
-}
-
-local function escape(s)
- return s:gsub("[<>&'\"]",entity_escapes)
-end
local safestr_mt = {name='SAFESTR', __tostring=function(x) return x.s end}
local function safestr(s) return setmetatable({s=s}, safestr_mt) end
diff --git a/xml_old.lua b/xml_old.lua
new file mode 100644
index 0000000..a63c0f7
--- /dev/null
+++ b/xml_old.lua
@@ -0,0 +1,48 @@
+function parseargs(s)
+ local arg = {}
+ string.gsub(s, "([%-%w]+)=([\"'])(.-)%2", function (w, _, a)
+ arg[w] = a
+ end)
+ return arg
+end
+
+function collect(s)
+ local stack = {}
+ local top = {}
+ table.insert(stack, top)
+ local ni,c,label,xarg, empty
+ local i, j = 1, 1
+ while true do
+ ni,j,c,label,xarg, empty = string.find(s, "<(%/?)([%w:]+)(.-)(%/?)>", i)
+ if not ni then break end
+ local text = string.sub(s, i, ni-1)
+ if not string.find(text, "^%s*$") then
+ table.insert(top, text)
+ end
+ if empty == "/" then -- empty element tag
+ table.insert(top, {label=label, xarg=parseargs(xarg), empty=1})
+ elseif c == "" then -- start tag
+ top = {label=label, xarg=parseargs(xarg)}
+ table.insert(stack, top) -- new level
+ else -- end tag
+ local toclose = table.remove(stack) -- remove top
+ top = stack[#stack]
+ if #stack < 1 then
+ error("nothing to close with "..label)
+ end
+ if toclose.label ~= label then
+ error("trying to close "..toclose.label.." with "..label)
+ end
+ table.insert(top, toclose)
+ end
+ i = j+1
+ end
+ local text = string.sub(s, i)
+ if not string.find(text, "^%s*$") then
+ table.insert(stack[#stack], text)
+ end
+ if #stack > 1 then
+ error("unclosed "..stack[#stack].label)
+ end
+ return stack[1]
+end
diff --git a/xmpp.lua b/xmpp.lua
index 84400bb..24a5a3c 100644
--- a/xmpp.lua
+++ b/xmpp.lua
@@ -1,26 +1,51 @@
-local xmpp_conf = {
- jid='wilson@ubq323.website',
- server='ubq323.website',
- auth='AHdpbHNvbgBncmVnb3J5PDM=',
- resource='cheese',
- muc='d@conference.ubq323.website',
-}
local cqueues = require'cqueues'
+local cqaux = require'cqueues.auxlib'
local socket = require'cqueues.socket'
local xml = require'xml'
local X = xml.X
local xmlify = xml.xmlify
local pprint=require'pprint'
-local discord=require'discord'
+local Queue = require'queue'
+local base64 = require'base64'
+local sha1 = require'sha1'
+local Xmpp = {}
-local function connect(conf)
- local sock = assert(socket.connect(conf.server, 5222))
+local function make_auth(authz, authn, password)
+ -- sasl plain (RFC4616)
+ return base64.encode(authz..'\0'..authn..'\0'..password)
+end
+
+function Xmpp.makepylon(pylonname, conf, cq, network)
+ local self = {
+ pylonname = pylonname,
+ cq = cq,
+ network = network,
+ inbox = Queue.make(),
+ }
+ local function conf_var(name)
+ assert(conf[name] ~= nil, 'missing conf field '..name)
+ self[name] = conf[name]
+ end
+ conf_var 'jid'
+ conf_var 'server'
+ conf_var 'resource'
+ conf_var 'component'
+ conf_var 'component_secret'
+
+ setmetatable(self, {__index=Xmpp})
+ return self
+
+end
+
+function Xmpp._connect_c2s(self)
+ local sock = assert(socket.connect(self.server, 5222))
+ self.sock = sock
sock:setmode('bn','bn')
local start = ([[
-<?xml version='1.0'?><stream:stream from='%s' to='%s' version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>]]):format(conf.jid, conf.server)
+<?xml version='1.0'?><stream:stream from='%s' to='%s' version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>]]):format(self.jid, self.server)
-- state of the art xml parser
local function check_and_send(test, text)
@@ -35,68 +60,125 @@ local function connect(conf)
check_and_send('proceed', nil)
sock:starttls()
sock:write(start)
+ local auth = make_auth('', self.jid:match"(.*)@", self.password)
check_and_send('PLAIN',
- xmlify(X.auth{xmlns=ietf_urn"sasl", mechanism='PLAIN', conf.auth}))
+ xmlify(X.auth{xmlns=ietf_urn"sasl", mechanism='PLAIN', auth}))
check_and_send('success',start)
check_and_send('bind',
xmlify(X.iq{type='set', id='aaaa',
- X.bind{xmlns=ietf_urn"bind", X.resource{conf.resource}}}))
+ X.bind{xmlns=ietf_urn"bind", X.resource{self.resource}}}))
check_and_send('jid',X.presence{X.show{'chat'}})
return sock
end
+-- this sucks! no tls! no security! 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)
--- local mucs = {
--- 'ja@conference.ubq323.website',
--- 'a@conference.ubq323.website',
--- }
-
-
-local function run_xmpp(cq)
- local sock = connect(xmpp_conf)
+ print(start)
- -- sock:write(xmlify(
- -- X.presence{to=xmpp_conf.muc..'/wilson',
- -- X.x{xmlns='http://jabber.org/protocol/muc',
- -- X.history{maxstanzas='0'}}}))
+ -- 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')
+ print('streamhead', streamhead)
+ 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)
- -- cq:wrap(function()
- -- local n = 1
- -- while true do
- -- sock:write(xmlify(
- -- X.message{to=xmpp_conf.muc, type='groupchat',
- -- id='wilson_episode_'..n,
- -- X.body{'i too am in episode '..n}}))
- -- n=n+1
- -- cqueues.sleep(60)
- -- end
- -- end)
+ return sock
+end
- sock:write(xmlify(
- X.iq{type='get',to=xmpp_conf.muc, id='info1',
- X.query{xmlns='http://jabber.org/protocol/disco#info'}}))
+local THE_MUC = 'd@conference.ubq323.website'
+function Xmpp.run(self)
+ self:_connect_component()
+ self.cq:wrap(self.recving, self)
+ self.cq:wrap(self.sending, self)
+end
- for x in xml.stanzae(function() return sock:read('-2048') end) do
+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
- pprint('M', x.xarg.from, body)
- if body == 'hi wilson' then
- sock:write(xmlify(
- X.message{to=xmpp_conf.muc, type='groupchat',
- X.body{'hi '..x.xarg.from..'.'}}))
+ local fr = x.xarg.from
+ local t = x.xarg.to
+ if body and fr:match"/" and t == 'wilson@'..self.component then
+ self.network:post(self.pylonname, THE_MUC, {
+ body = body,
+ source = '[x]'..x.xarg.from:match("/(.*)")
+ })
end
- -- discord.grom(x.xarg.from, body or '*(there\'s no body here)*')
end
end
end
-local cq = cqueues.new()
-cq:wrap(run_xmpp, cq)
+function Xmpp.sending(self)
+ local users_inuse = {}
+ local nicks_inuse = {}
+ local function ensure_joined(muc,user,nick)
+ if 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
+ 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 ch, msg in self.inbox:iter() do
+ pprint(ch,msg)
+ ensure_joined(THE_MUC, msg.source, msg.source)
+ local user = msg.source: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{msg.body}}))
+ end
+end
+
+
+return Xmpp
+
+-- local cq = cqueues.new()
+-- local conf = {
+-- jid='wilson@ubq323.website',
+-- server='ubq323.website',
+-- password='gregory<3',
+-- resource='cheese',
+-- }
+-- local dummy_network = {
+-- post = function(self, pylonname, channel, message)
+-- pprint(pylonname, channel, message)
+-- end
+-- }
+-- local pylon = Xmpp.makepylon('xmpptest',conf, cq, dummy_network)
+-- pylon:run()
-pprint(cq:loop())
+-- pprint('peas', cq:loop())