--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/ibb.lua Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,188 @@
+
+-- IN-BAND BYTESTREAMS (XEP-0047)
+
+-- TODO bidirectionality
+-- thus, on stream accept we can add our sid to incoming files structure,
+-- as if we received and accepted incoming request.
+-- message stanzas
+
+-- library
+
+local lm = require 'lm'
+local iq = require 'lm.iq'
+local base64 = require 'base64'
+
+--
+
+local F = { }
+local M = { }
+M.__index = M
+local O = {
+ handler =
+ function ( accept, reject )
+ reject ()
+ end,
+}
+
+function F.new ( conn, to, bs, sid )
+ local obj = {
+ conn = conn,
+ to = to,
+ sbs = bs,
+ bs = math.floor ( bs * 3 / 4 ),
+ sid = sid,
+ seq = 0,
+ }
+ setmetatable ( obj, M )
+ return obj
+end
+
+function M.open ( obj, success, fail )
+ iq.send ( obj.conn, obj.to, 'set',
+ {
+ open = { sid = obj.sid, ['block-size'] = obj.sbs, xmlns = 'http://jabber.org/protocol/ibb' }
+ }, success, fail )
+end
+
+function M.send ( obj, data, success, fail )
+ if data and data ~= '' then
+ local start = 0
+ while start < data:len () do
+ local chunk = base64.encode ( data:sub ( start, obj.bs ) )
+ local cseq = obj.seq -- local instance
+ iq.send ( obj.conn, obj.to, 'set',
+ {
+ data = { sid = obj.sid, xmlns = 'http://jabber.org/protocol/ibb', seq = cseq,
+ chunk,
+ },
+ },
+ function ()
+ success ( cseq )
+ end,
+ function ( mesg )
+ noerr = false
+ fail ( mesg )
+ end )
+ start = start + obj.bs
+ obj.seq = obj.seq + 1
+ end
+ end
+end
+
+function M.close ( obj, success, fail )
+ iq.send ( obj.conn, obj.to, 'set',
+ {
+ close = { sid = obj.sid, xmlns = 'http://jabber.org/protocol/ibb' },
+ }, success, fail )
+end
+
+function F.handler ( handler )
+ O.handler = handler
+end
+
+local ibb_files = {}
+
+function F.iq_handler ( conn, mess )
+ local mtype, smtype = mess:type ()
+ if smtype ~= 'set' then
+ return false
+ end
+
+ local child = mess:child ()
+ if not child or child:attribute ( 'xmlns' ) ~= 'http://jabber.org/protocol/ibb' then
+ return false
+ end
+
+ local id = mess:attribute ( 'id' )
+ local from = mess:attribute ( 'from' )
+ local action = child:name ()
+ local sid = child:attribute ( 'sid' )
+
+ if action == 'open' then
+ if not ibb_files[sid] then
+ O.handler ( from,
+ function ( success, fail )
+ ibb_files[sid] = { from = from, success = success, fail = fail }
+ conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
+ end,
+ function ()
+ conn:send (
+ lm.message.create { to = from, mtype = 'iq-error', id = id,
+ error = { code = '405', type = 'cancel',
+ ['not-allowed'] = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
+ },
+ } )
+ end )
+ else
+ conn:send (
+ lm.message.create { to = from, mtype = 'iq-error', id = id,
+ error = { code = '409', type = 'cancel',
+ conflict = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
+ },
+ } )
+ end
+ elseif action == 'data' then
+ local seq = child:attribute ( 'seq' )
+ if ibb_files[sid] and from == ibb_files[sid].from and not ibb_files[sid][tonumber(seq)+1] then
+ local data = child:value ()
+ conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
+ ibb_files[sid][tonumber(seq)+1] = data
+-- XXX ibb_files[sid].success ( seq )
+ else
+ if ibb_files[sid] then
+ ibb_files[sid].fail ( 'conflict' )
+ ibb_files[sid] = nil -- invalidate session
+ conn:send (
+ lm.message.create { to = from, mtype = 'iq-error', id = id,
+ error = { code = '409', type = 'cancel',
+ conflict = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
+ },
+ } )
+ else
+ conn:send (
+ lm.message.create { to = from, mtype = 'iq-error', id = id,
+ error = { code = '404', type = 'cancel', -- XXX: check
+ ['item-not-found'] = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
+ },
+ } )
+ end
+ end
+ elseif action == 'close' then
+ if ibb_files[sid] and from == ibb_files[sid].from then
+ local data = ''
+ for seq, chunk in ipairs ( ibb_files[sid] ) do
+ data = data .. chunk
+ end
+ local decoded = base64.decode ( data )
+ ibb_files[sid].success ( decoded )
+ conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
+ ibb_files[sid] = nil
+ else
+ if ibb_files[sid] then
+ ibb_files[sid].fail ( 'conflict' )
+ ibb_files[sid] = nil -- invalidate session
+ conn:send (
+ lm.message.create { to = from, mtype = 'iq-error', id = id,
+ error = { code = '409', type = 'cancel',
+ conflict = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
+ },
+ } )
+ else
+ conn:send (
+ lm.message.create { to = from, mtype = 'iq-error', id = id,
+ error = { code = '404', type = 'cancel', -- XXX: check
+ ['item-not-found'] = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
+ },
+ } )
+ end
+ end
+ else
+ return false
+ end
+
+ return true
+end
+
+return F
+
+-- vim: se ts=4: --