Class Jabber::FileTransfer::Helper
In: lib/xmpp4r/bytestreams/helper/filetransfer.rb
Parent: Object

The FileTransfer helper provides the ability to respond to incoming and to offer outgoing file-transfers.

Methods

Attributes

allow_bytestreams  [RW]  Set this to false if you don‘t want to use SOCKS5Bytestreams
allow_ibb  [RW]  Set this to false if you don‘t want to use IBB
my_jid  [RW]  Set this if you want to use this helper in a Component

Public Class methods

Create a new FileTransfer instance

[Source]

     # File lib/xmpp4r/bytestreams/helper/filetransfer.rb, line 140
140:       def initialize(stream)
141:         @stream = stream
142:         @my_jid = nil
143:         @allow_bytestreams = true
144:         @allow_ibb = true
145: 
146:         @incoming_cbs = CallbackList.new
147: 
148:         @stream.add_iq_callback(150, self) { |iq|
149:           if iq.type == :set
150:             file = iq.first_element('si/file')
151:             field = nil
152:             iq.each_element('si/feature/x') { |e| field = e.field('stream-method') }
153: 
154:             if file and field
155:               @incoming_cbs.process(iq, file)
156:               true
157:             else
158:               false
159:             end
160:           else
161:             false
162:           end
163:         }
164:       end

Public Instance methods

Accept an incoming file-transfer, to be used in a block given to add_incoming_callback

offset and length will be ignored if there is no ‘si/file/range’ in iq.

iq:[Iq] of file-transfer we want to accept
offset:[Fixnum] or [nil]
length:[Fixnum] or [nil]
result:[Bytestreams::SOCKS5BytestreamsTarget] or [Bytestreams::IBBTarget] or [nil] if no valid stream-method

[Source]

     # File lib/xmpp4r/bytestreams/helper/filetransfer.rb, line 187
187:       def accept(iq, offset=nil, length=nil)
188:         oldsi = iq.first_element('si')
189: 
190:         answer = iq.answer(false)
191:         answer.type = :result
192: 
193:         si = answer.add(Bytestreams::IqSi.new)
194:         if (offset or length) and oldsi.file.range
195:           si.add(Bytestreams::IqSiFile.new)
196:           si.file.add(Bytestreams::IqSiFileRange.new(offset, length))
197:         end
198:         si.add(FeatureNegotiation::IqFeature.new.import(oldsi.feature))
199:         si.feature.x.type = :submit
200:         stream_method = si.feature.x.field('stream-method')
201: 
202:         if stream_method.options.keys.include?(Bytestreams::IqQueryBytestreams::NS_BYTESTREAMS) and @allow_bytestreams
203:           stream_method.values = [Bytestreams::IqQueryBytestreams::NS_BYTESTREAMS]
204:           stream_method.options = []
205:           @stream.send(answer)
206: 
207:           Bytestreams::SOCKS5BytestreamsTarget.new(@stream, oldsi.id, iq.from, iq.to)
208:         elsif stream_method.options.keys.include?(Bytestreams::IBB::NS_IBB) and @allow_ibb
209:           stream_method.values = [Bytestreams::IBB::NS_IBB]
210:           stream_method.options = []
211:           @stream.send(answer)
212: 
213:           Bytestreams::IBBTarget.new(@stream, oldsi.id, iq.from, iq.to)
214:         else
215:           eanswer = iq.answer(false)
216:           eanswer.type = :error
217:           eanswer.add(Error.new('bad-request')).type = :cancel
218:           eanswer.error.add(REXML::Element.new('no-valid-streams')).add_namespace('http://jabber.org/protocol/si')
219:           @stream.send(eanswer)
220: 
221:           nil
222:         end
223:       end

Add a callback which will be invoked upon an incoming file-transfer

block takes two arguments:

You may then invoke accept or decline

[Source]

     # File lib/xmpp4r/bytestreams/helper/filetransfer.rb, line 173
173:       def add_incoming_callback(priority = 0, ref = nil, &block)
174:         @incoming_cbs.add(priority, ref, block)
175:       end

Decline an incoming file-transfer, to be used in a block given to add_incoming_callback

iq:[Iq] of file-transfer we want to decline

[Source]

     # File lib/xmpp4r/bytestreams/helper/filetransfer.rb, line 229
229:       def decline(iq)
230:         answer = iq.answer(false)
231:         answer.type = :error
232:         error = answer.add(Error.new('forbidden', 'Offer declined'))
233:         error.type = :cancel
234:         @stream.send(answer)
235:       end

Offer a file to somebody

Will wait for a response from the peer

The result is a stream which you can configure, or nil if the peer responded with an invalid stream-method.

May raise an ErrorException

jid:[JID] to send the file to
source:File-transfer source, implementing the FileSource interface
desc:[String] or [nil] Optional file description
from:[String] or [nil] Optional jid for components
result:[Bytestreams::SOCKS5BytestreamsInitiator] or [Bytestreams::IBBInitiator] or [nil]

[Source]

     # File lib/xmpp4r/bytestreams/helper/filetransfer.rb, line 251
251:       def offer(jid, source, desc=nil, from=nil)
252:         from = from || @my_jid || @stream.jid
253:         session_id = Jabber::IdGenerator.instance.generate_id
254: 
255:         offered_methods = {}
256:         if @allow_bytestreams
257:           offered_methods[Bytestreams::IqQueryBytestreams::NS_BYTESTREAMS] = nil
258:         end
259:         if @allow_ibb
260:           offered_methods[Bytestreams::IBB::NS_IBB] = nil
261:         end
262: 
263:         iq = Iq::new(:set, jid)
264:         iq.from = from
265:         si = iq.add(Bytestreams::IqSi.new(session_id, Bytestreams::IqSi::PROFILE_FILETRANSFER, source.mime))
266: 
267:         file = si.add(Bytestreams::IqSiFile.new(source.filename, source.size))
268:         file.hash = source.md5
269:         file.date = source.date
270:         file.description = desc if desc
271:         file.add(Bytestreams::IqSiFileRange.new) if source.can_range?
272: 
273:         feature = si.add(REXML::Element.new('feature'))
274:         feature.add_namespace 'http://jabber.org/protocol/feature-neg'
275:         x = feature.add(Dataforms::XData.new(:form))
276:         stream_method_field = x.add(Dataforms::XDataField.new('stream-method', :list_single))
277:         stream_method_field.options = offered_methods
278: 
279:         begin
280:           stream_method = nil
281:           response = nil
282:           @stream.send_with_id(iq) { |r|
283:             response = r
284:             si = response.first_element('si')
285:             if response.type == :result and si and si.feature and si.feature.x
286:               stream_method = si.feature.x.field('stream-method').values.first
287: 
288:               if si.file and si.file.range
289:                 if source.can_range?
290:                   source.seek(si.file.range.offset) if si.file.range.offset
291:                   source.length = si.file.range.length if si.file.range.length
292:                 else
293:                   source.read(si.file.range.offset)
294:                 end
295:               end
296:             end
297:             true
298:           }
299:         rescue ErrorException => e
300:           if e.error.code == 403  # Declined
301:             return false
302:           else
303:             raise e
304:           end
305:         end
306: 
307:         if stream_method == Bytestreams::IqQueryBytestreams::NS_BYTESTREAMS and @allow_bytestreams
308:           Bytestreams::SOCKS5BytestreamsInitiator.new(@stream, session_id, from, jid)
309:         elsif stream_method == Bytestreams::IBB::NS_IBB and @allow_ibb
310:           Bytestreams::IBBInitiator.new(@stream, session_id, from, jid)
311:         else  # Target responded with a stream_method we didn't offer
312:           eanswer = response.answer
313:           eanswer.type = :error
314:           eanswer.add Error::new('bad-request')
315:           @stream.send(eanswer)
316:           nil
317:         end
318:       end

[Validate]