scripts/xep0047.lua
changeset 4 bca17e4a9851
equal deleted inserted replaced
3:a5f864d4207f 4:bca17e4a9851
       
     1 
       
     2 receiving_files = {}
       
     3 ibb_block_size  = 4096
       
     4 current_sid_number = 0
       
     5 
       
     6 -- FIXME: read from /dev/urandom?
       
     7 function gen_unique_sid ()
       
     8 	current_sid_number = current_sid_number + 1
       
     9 	return 'mc-' .. tostring ( current_sid_number )
       
    10 end
       
    11 
       
    12 ibb_incoming_iq_handler = lm.message_handler.new ( 
       
    13 	function ( conn, mess )
       
    14 		local id   = mess:attribute ( 'id' )
       
    15 		local from = mess:attribute ( 'from' )
       
    16 		if mess:child ( 'open' ) and mess:child( 'open' ):attribute ( 'xmlns' ) == 'http://jabber.org/protocol/ibb' then
       
    17 			local sid  = mess:child( 'open' ):attribute ( 'sid' )
       
    18 			if not receiving_files[sid] then
       
    19 				local buffer = ''
       
    20 				receiving_files[sid] = { from = from, status = 'pending' }
       
    21 				receiving_files[sid].accept =
       
    22 					function ( name )
       
    23 						main.print_info ( from, string.format ( "Receiving stream from %s, id %s", from, sid ) )
       
    24 						conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
       
    25 						receiving_files[sid].name = name
       
    26 						receiving_files[sid].status = 'accepted'
       
    27 					end
       
    28 				receiving_files[sid].reject =
       
    29 					function ()
       
    30 						conn:send (
       
    31 							lm.message.create { to = from, mtype = 'iq-error', id = id,
       
    32 								error = { code = '405', type = 'cancel',
       
    33 									['not-allowed'] = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' }
       
    34 								}
       
    35 							} )
       
    36 						receiving_files[sid].status = 'rejected'
       
    37 					end
       
    38 				print ( 'You have a new bytestream to receive. To save it use /ibb accept ' .. sid .. ' filename' )
       
    39 			else
       
    40 				conn:send (
       
    41 					lm.message.create { to = from, mtype = 'iq-error', id = id,
       
    42 						error = { code = '409', type = 'cancel',
       
    43 							conflict = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' }
       
    44 						}
       
    45 					} )
       
    46 			end
       
    47 		elseif mess:child ( 'data' ) and mess:child( 'data' ):attribute ( 'xmlns' ) == 'http://jabber.org/protocol/ibb' then
       
    48 			local sid  = mess:child( 'data' ):attribute ( 'sid' )
       
    49 			local seq  = mess:child( 'data' ):attribute ( 'seq' )
       
    50 			if receiving_files[sid] and from == receiving_files[sid].from and not receiving_files[sid][tonumber(seq)+1] then
       
    51 				local data = mess:child( 'data' ):value ()
       
    52 				main.print_info ( from, string.format ( " - stream part %s, id %s, %d bytes", seq, sid, data:len() ) )
       
    53 				conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
       
    54 				receiving_files[sid][tonumber(seq)+1] = data
       
    55 			else
       
    56 				receiving_files[sid] = nil -- invalidate session
       
    57 				conn:send (
       
    58 					lm.message.create { to = from, mtype = 'iq-error', id = id,
       
    59 						error = { code = '409', type = 'cancel',
       
    60 							conflict = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' }
       
    61 						}
       
    62 					} )
       
    63 			end
       
    64 		elseif mess:child ( 'close' ) and mess:child( 'close' ):attribute ( 'xmlns' ) == 'http://jabber.org/protocol/ibb' then
       
    65 			local sid  = mess:child( 'close' ):attribute ( 'sid' )
       
    66 			if receiving_files[sid] and from == receiving_files[sid].from then
       
    67 				main.print_info ( from, "Done with stream id " .. sid )
       
    68 				conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
       
    69 				local decoder = io.popen ( string.format ( "base64 -d -i >%q", receiving_files[sid].name ), "w" )
       
    70 				if not decoder then
       
    71 					main.print_info ( from, "Error opening decoder" )
       
    72 				else
       
    73 					for i, v in ipairs ( receiving_files[sid] ) do
       
    74 						decoder:write ( v )
       
    75 					end
       
    76 					decoder:close ()
       
    77 				end
       
    78 			else
       
    79 				receiving_files[sid] = nil -- invalidate session
       
    80 				conn:send (
       
    81 					lm.message.create { to = from, mtype = 'iq-error', id = id,
       
    82 						error = { code = '409', type = 'cancel',
       
    83 							conflict = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' }
       
    84 						}
       
    85 					} )
       
    86 			end
       
    87 		else
       
    88 			return false
       
    89 		end
       
    90 		return true
       
    91 	end )
       
    92 
       
    93 -- You must specify a full jid with resource!
       
    94 function send_file ( to, name )
       
    95 	local sid = gen_unique_sid ()
       
    96 	local conn = lm.connection.bless ( main.connection () )
       
    97 	conn:send (
       
    98 		lm.message.create { to = to, mtype = 'iq-set',
       
    99 			open = { sid = sid, ['block-size'] = ibb_block_size, xmlns = 'http://jabber.org/protocol/ibb' }
       
   100 		},
       
   101 		function ( conn, message )
       
   102 			if message:child ( 'error' ) then
       
   103 				main.print_info ( to, "Stream request refused: " .. message:child( 'error' ):children():name () )
       
   104 			else
       
   105 				main.print_info ( to, "Stream accepted, starting sequence" )
       
   106 				local buffer = ''
       
   107 				main.bgread ( string.format ( 'base64 -w 0 %q', name ),
       
   108 					function ( data )
       
   109 						if data then
       
   110 							buffer = buffer .. data
       
   111 							return true
       
   112 						else
       
   113 							local seq = 0
       
   114 							local msgbuf = buffer:sub ( 1, ibb_block_size )
       
   115 							buffer = buffer:sub ( ibb_block_size + 1 )
       
   116 							local function handler ( conn, message )
       
   117 								if message:child ( 'error' ) then
       
   118 									main.print_info ( to, "Stream error, transfer ceased at seq = " .. seq .. ": " .. message:child( 'error' ):children():name () )
       
   119 								else
       
   120 									main.print_info ( to, " - acquired seq = " .. seq )
       
   121 									seq = seq + 1
       
   122 									if buffer:len () == 0 then
       
   123 										conn:send (
       
   124 											lm.message.create { to = to, mtype = 'iq-set',
       
   125 												close = { sid = sid, xmlns = 'http://jabber.org/protocol/ibb' }
       
   126 											},
       
   127 											function ( conn, message )
       
   128 												if message:child ( 'error' ) then
       
   129 													main.print_info ( to, "Error at closing stream: " .. message:child( 'error' ):children():name () )
       
   130 												else
       
   131 													main.print_info ( to, "File successfully transferred" )
       
   132 												end
       
   133 												return true
       
   134 											end )
       
   135 									else
       
   136 										local msgbuf = buffer:sub ( 1, ibb_block_size )
       
   137 										buffer = buffer:sub ( ibb_block_size )
       
   138 										conn:send (
       
   139 											lm.message.create { to = to, mtype = 'iq-set',
       
   140 												data = { sid = sid, xmlns = 'http://jabber.org/protocol/ibb', seq = seq,
       
   141 													msgbuf
       
   142 												}
       
   143 											},
       
   144 											handler )
       
   145 									end
       
   146 								end
       
   147 								return true
       
   148 							end
       
   149 							conn:send (
       
   150 								lm.message.create { to = to, mtype = 'iq-set',
       
   151 									data = { sid = sid, xmlns = 'http://jabber.org/protocol/ibb', seq = seq,
       
   152 										msgbuf
       
   153 									}
       
   154 								},
       
   155 								handler )
       
   156 							return false
       
   157 						end
       
   158 					end )
       
   159 			end
       
   160 			return true
       
   161 		end )
       
   162 end
       
   163 
       
   164 main.add_command ( 'ibb',
       
   165 	function ( args )
       
   166 		args = parse_args ( args )
       
   167 		if args[1] == 'send' then
       
   168 			local who
       
   169 			if args.t then
       
   170 				who = args.t
       
   171 				args.t = nil
       
   172 			else
       
   173 				who = full_current_jid ()
       
   174 			end
       
   175 			args[1] = nil
       
   176 			send_file ( who, rebuild_args_string ( args ) )
       
   177 		elseif args[1] == 'accept' then
       
   178 			local id = args[2]
       
   179 			args[1] = nil
       
   180 			args[2] = nil
       
   181 			if receiving_files[id] then
       
   182 				receiving_files[id].accept ( rebuild_args_string ( args ) )
       
   183 			end
       
   184 		elseif args[1] == 'reject' then
       
   185 			local id = args[2]
       
   186 			if receiving_files[id] then
       
   187 				receiving_files[id].reject ()
       
   188 			end
       
   189 		elseif args[1] == 'del' then
       
   190 			local id = args[2]
       
   191 			receiving_files[id] = nil
       
   192 		else
       
   193 			print ( 'List of incoming streams:' )
       
   194 			for sid, data in pairs ( receiving_files ) do
       
   195 				print ( sid .. ': ' .. ( data.name or '(not set)' ) .. ' [' .. data.status .. ']' )
       
   196 			end
       
   197 		end
       
   198 	end )
       
   199 
       
   200 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."
       
   201 
       
   202 -- vim: se ts=4: --