diff options
-rw-r--r-- | base64.lua | 38 | ||||
-rw-r--r-- | irc.lua | 138 | ||||
-rw-r--r-- | main.lua | 88 | ||||
-rw-r--r-- | queue.lua | 8 | ||||
-rw-r--r-- | rirc.lua | 2 | ||||
-rw-r--r-- | sha1.lua | 169 | ||||
-rw-r--r-- | test.lua | 24 | ||||
-rw-r--r-- | wilson.ini | 33 | ||||
-rw-r--r-- | xml.lua | 34 | ||||
-rw-r--r-- | xml_old.lua | 48 | ||||
-rw-r--r-- | xmpp.lua | 180 |
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} @@ -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()) @@ -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() @@ -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() @@ -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@ + @@ -2,6 +2,23 @@ -- modified by me a bit +entity_escapes = { + ["<"]="<", + [">"]=">", + ["&"]="&", + ['"']=""", + ["'"]="'" +} +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 = { - ["<"]="<", - [">"]=">", - ["&"]="&", - ['"']=""", - ["'"]="'" -} - -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 @@ -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()) |