--- a/examples/ibb.lua Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/ibb.lua Tue Mar 31 18:35:34 2009 +0300
@@ -1,188 +1,138 @@
-
--- 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
+local lm = require 'lm'
+local ibb = require 'lm.ibb'
--- library
+local mc_incoming_files = { }
-local lm = require 'lm'
-local iq = require 'iq'
-local base64 = require 'base64'
-
---
+ibb.handler (
+ function ( from, accept, reject )
+ local fid = #mc_incoming_files + 1
+ mc_incoming_files[fid] = {
+ from = from,
+ accept =
+ function ( name )
+ mc_incoming_files[fid].name = name
+ accept (
+ function ( data )
+ local h = io.open ( mc_incoming_files[fid].name, 'w' )
+ if not h then
+ print ( 'Cannot open output file: ' .. mc_incoming_files[fid].name )
+ return
+ end
+ h:write ( data )
+ h:close ()
+ print ( 'Stream ' .. fid .. ' successfully saved to ' .. mc_incoming_files[fid].name )
+ mc_incoming_files[fid] = nil
+ end,
+ function ( mesg )
+ main.print_info ( from, 'Stream error: ' .. mesg )
+ mc_incoming_files[fid] = nil -- XXX
+ end )
+ end,
+ reject =
+ function ()
+ reject ()
+ print ( 'Stream ' .. fid .. ' rejected' )
+ mc_incoming_files[fid] = nil
+ end,
+ }
+ main.print_info ( from, from .. ' wants you to receive stream. Use /ibb [accept|reject] ' .. fid .. ' to process his request.' )
+ end )
-local F = { }
-local M = { }
-M.__index = M
-local O = {
- handler =
- function ( accept, reject )
- reject ()
- end,
-}
+local ibb_sid = 0
-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,
- },
- },
+main.command ( 'ibb',
+ function ( args )
+ local action = args[1]
+ if action == 'send' then
+ local who
+ if args.t then
+ who = args.t
+ else
+ who = main.full_jid ()
+ end
+ local fname = args[2]
+ local conn = lm.connection.bless ( main.connection () )
+ local sid = ibb_sid
+ ibb_sid = ibb_sid + 1
+ local stream = ibb.new ( conn, who, 4096, sid )
+ stream:open (
function ()
- success ( cseq )
+ main.print_info ( who, 'Stream accepted' )
+ local noerr = true
+ local h = io.open ( fname, 'r' )
+ if not h then
+ print ( 'Cannot open file ' .. fname )
+ return
+ end
+ local data = h:read ( '*a' ) -- In fact, it is better to read it in chunks :/
+ h:close ()
+ local fail =
+ function ( mesg )
+ noerr = false
+ main.print_info ( who, 'Stream error: ' .. mesg )
+ end
+ stream:send ( data,
+ function ( seq )
+ main.print_info ( who, 'Delivery notification of chunk #' .. seq )
+ end, fail )
+ if noerr then
+ stream:close (
+ function ()
+ main.print_info ( who, 'Stream finalizing notification' )
+ end, fail )
+ end
+ if noerr then
+ main.print_info ( who, 'Stream sent' )
+ else
+ main.print_info ( who, 'Stream error occured' )
+ end
end,
function ( mesg )
- noerr = false
- fail ( mesg )
+ main.print_info ( who, 'Stream initiation error: ' .. mesg )
end )
- start = start + obj.bs
- obj.seq = obj.seq + 1
+ elseif action == 'accept' then
+ local id = tonumber(args[2])
+ if mc_incoming_files[id] then
+ mc_incoming_files[id].accept ( args[3] )
+ end
+ elseif action == 'reject' then
+ local id = tonumber(args[2])
+ if mc_incoming_files[id] then
+ mc_incoming_files[id].reject ()
+ end
+ else
+ local text = ''
+ for sid, data in pairs ( mc_incoming_files ) do
+ text = text .. '\n' .. sid .. ': ' .. data.from .. ' --> ' .. ( data.name or '?' )
+ end
+ if text ~= '' then
+ print ( 'List of incoming streams:' .. text )
+ else
+ print ( 'No streams' )
+ end
end
- end
-end
+ end, true, { "send", "accept", "reject" } )
-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
+
+commands_help['ibb'] = "[[-t target_jid] send filename | accept sid filename | reject sid]\n\nRequests, accepts or rejects sending file via in-band bytestream."
+
+local ibb_handler = lm.message_handler.new ( ibb.iq_handler )
+local ibb_handler_registered = false
-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
+hooks_d['hook-post-connect'].ibb =
+ function ( args )
+ lm.connection.bless( main.connection () ):handler ( ibb_handler, 'iq', 'normal' )
+ ibb_handler_registered = true
+ hooks_d['hook-post-connect'].ibb = nil
+ hooks_d['hook-quit'].ibb =
+ function ( args )
+ if ibb_handler_registered then
+ lm.connection.bless( main.connection () ):handler ( ibb_handler, 'iq' )
+ end
+ end
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
+main.add_feature ( 'http://jabber.org/protocol/ibb' )
-- vim: se ts=4: --