|
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: -- |