Completion
* commands completion
* common hooks lua interface
* complete separation of features into modules
receiving_files = {}
ibb_block_size = 4096
current_sid_number = 0
-- FIXME: read from /dev/urandom?
function gen_unique_sid ()
current_sid_number = current_sid_number + 1
return 'mc-' .. tostring ( current_sid_number )
end
ibb_incoming_iq_handler = lm.message_handler.new (
function ( conn, mess )
local id = mess:attribute ( 'id' )
local from = mess:attribute ( 'from' )
if mess:child ( 'open' ) and mess:child( 'open' ):attribute ( 'xmlns' ) == 'http://jabber.org/protocol/ibb' then
local sid = mess:child( 'open' ):attribute ( 'sid' )
if not receiving_files[sid] then
local buffer = ''
receiving_files[sid] = { from = from, status = 'pending' }
receiving_files[sid].accept =
function ( name )
main.print_info ( from, string.format ( "Receiving stream from %s, id %s", from, sid ) )
conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
receiving_files[sid].name = name
receiving_files[sid].status = 'accepted'
end
receiving_files[sid].reject =
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' }
}
} )
receiving_files[sid].status = 'rejected'
end
print ( 'You have a new bytestream to receive. To save it use /ibb accept ' .. sid .. ' filename' )
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 mess:child ( 'data' ) and mess:child( 'data' ):attribute ( 'xmlns' ) == 'http://jabber.org/protocol/ibb' then
local sid = mess:child( 'data' ):attribute ( 'sid' )
local seq = mess:child( 'data' ):attribute ( 'seq' )
if receiving_files[sid] and from == receiving_files[sid].from and not receiving_files[sid][tonumber(seq)+1] then
local data = mess:child( 'data' ):value ()
main.print_info ( from, string.format ( " - stream part %s, id %s, %d bytes", seq, sid, data:len() ) )
conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
receiving_files[sid][tonumber(seq)+1] = data
else
receiving_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' }
}
} )
end
elseif mess:child ( 'close' ) and mess:child( 'close' ):attribute ( 'xmlns' ) == 'http://jabber.org/protocol/ibb' then
local sid = mess:child( 'close' ):attribute ( 'sid' )
if receiving_files[sid] and from == receiving_files[sid].from then
main.print_info ( from, "Done with stream id " .. sid )
conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
local decoder = io.popen ( string.format ( "base64 -d -i >%q", receiving_files[sid].name ), "w" )
if not decoder then
main.print_info ( from, "Error opening decoder" )
else
for i, v in ipairs ( receiving_files[sid] ) do
decoder:write ( v )
end
decoder:close ()
end
else
receiving_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' }
}
} )
end
else
return false
end
return true
end )
-- You must specify a full jid with resource!
function send_file ( to, name )
local sid = gen_unique_sid ()
local conn = lm.connection.bless ( main.connection () )
conn:send (
lm.message.create { to = to, mtype = 'iq-set',
open = { sid = sid, ['block-size'] = ibb_block_size, xmlns = 'http://jabber.org/protocol/ibb' }
},
function ( conn, message )
if message:child ( 'error' ) then
main.print_info ( to, "Stream request refused: " .. message:child( 'error' ):children():name () )
else
main.print_info ( to, "Stream accepted, starting sequence" )
local buffer = ''
main.bgread ( string.format ( 'base64 -w 0 %q', name ),
function ( data )
if data then
buffer = buffer .. data
return true
else
local seq = 0
local msgbuf = buffer:sub ( 1, ibb_block_size )
buffer = buffer:sub ( ibb_block_size + 1 )
local function handler ( conn, message )
if message:child ( 'error' ) then
main.print_info ( to, "Stream error, transfer ceased at seq = " .. seq .. ": " .. message:child( 'error' ):children():name () )
else
main.print_info ( to, " - acquired seq = " .. seq )
seq = seq + 1
if buffer:len () == 0 then
conn:send (
lm.message.create { to = to, mtype = 'iq-set',
close = { sid = sid, xmlns = 'http://jabber.org/protocol/ibb' }
},
function ( conn, message )
if message:child ( 'error' ) then
main.print_info ( to, "Error at closing stream: " .. message:child( 'error' ):children():name () )
else
main.print_info ( to, "File successfully transferred" )
end
return true
end )
else
local msgbuf = buffer:sub ( 1, ibb_block_size )
buffer = buffer:sub ( ibb_block_size )
conn:send (
lm.message.create { to = to, mtype = 'iq-set',
data = { sid = sid, xmlns = 'http://jabber.org/protocol/ibb', seq = seq,
msgbuf
}
},
handler )
end
end
return true
end
conn:send (
lm.message.create { to = to, mtype = 'iq-set',
data = { sid = sid, xmlns = 'http://jabber.org/protocol/ibb', seq = seq,
msgbuf
}
},
handler )
return false
end
end )
end
return true
end )
end
main.command ( 'ibb',
function ( args )
args = parse_args ( args )
if args[1] == 'send' then
local who
if args.t then
who = args.t
args.t = nil
else
who = full_current_jid ()
end
args[1] = nil
send_file ( who, rebuild_args_string ( args ) )
elseif args[1] == 'accept' then
local id = args[2]
args[1] = nil
args[2] = nil
if receiving_files[id] then
receiving_files[id].accept ( rebuild_args_string ( args ) )
end
elseif args[1] == 'reject' then
local id = args[2]
if receiving_files[id] then
receiving_files[id].reject ()
end
elseif args[1] == 'del' then
local id = args[2]
receiving_files[id] = nil
else
print ( 'List of incoming streams:' )
for sid, data in pairs ( receiving_files ) do
print ( sid .. ': ' .. ( data.name or '(not set)' ) .. ' [' .. data.status .. ']' )
end
end
end, { "send", "accept", "reject", "del" } )
commands_help['ibb'] = "[[-t target_jid] send filename | accept sid filename | reject sid filename | del sid]\n\nRequests, accepts or rejects sending file via in-band bytestream."
hooks_d['hook-post-connect'].xep0047 =
function ( args )
lm.connection.bless( main.connection () ):handler ( ibb_incoming_iq_handler, 'iq', 'normal' )
main.add_feature ( 'http://jabber.org/protocol/ibb' )
ibb_handler_registered = true
hooks_d['hook-post-connect'].xep0047 = nil
hooks_d['hook-quit'].xep0047 =
function ( args )
if ibb_handler_registered then
lm.connection.bless( main.connection () ):handler ( ibb_incoming_iq_handler, 'iq' )
end
end
end
-- vim: se ts=4: --