The FileTransfer helper provides the ability to respond to incoming and to offer outgoing file-transfers.
Set this to false if you don't want to use SOCKS5Bytestreams
Set this to false if you don't want to use IBB
Set this if you want to use this helper in a Component
Create a new FileTransfer instance
# File lib/xmpp4r/bytestreams/helper/filetransfer.rb, line 139 def initialize(stream) @stream = stream @my_jid = nil @allow_bytestreams = true @allow_ibb = true @incoming_cbs = CallbackList.new @stream.add_iq_callback(150, self) { |iq| if iq.type == :set file = iq.first_element('si/file') field = nil iq.each_element('si/feature/x') { |e| field = e.field('stream-method') } if file and field @incoming_cbs.process(iq, file) true else false end else false end } end
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.
of file-transfer we want to accept
or [nil]
or [nil]
or [Bytestreams::IBBTarget] or [nil] if no valid stream-method
# File lib/xmpp4r/bytestreams/helper/filetransfer.rb, line 186 def accept(iq, offset=nil, length=nil) oldsi = iq.first_element('si') answer = iq.answer(false) answer.type = :result si = answer.add(Bytestreams::IqSi.new) if (offset or length) and oldsi.file.range si.add(Bytestreams::IqSiFile.new) si.file.add(Bytestreams::IqSiFileRange.new(offset, length)) end si.add(FeatureNegotiation::IqFeature.new.import(oldsi.feature)) si.feature.x.type = :submit stream_method = si.feature.x.field('stream-method') if stream_method.options.keys.include?(Bytestreams::NS_BYTESTREAMS) and @allow_bytestreams stream_method.values = [Bytestreams::NS_BYTESTREAMS] stream_method.options = [] @stream.send(answer) Bytestreams::SOCKS5BytestreamsTarget.new(@stream, oldsi.id, iq.from, iq.to) elsif stream_method.options.keys.include?(Bytestreams::IBB::NS_IBB) and @allow_ibb stream_method.values = [Bytestreams::IBB::NS_IBB] stream_method.options = [] @stream.send(answer) Bytestreams::IBBTarget.new(@stream, oldsi.id, iq.from, iq.to) else eanswer = iq.answer(false) eanswer.type = :error eanswer.add(ErrorResponse.new('bad-request')).type = :cancel eanswer.error.add(REXML::Element.new('no-valid-streams')).add_namespace('http://jabber.org/protocol/si') @stream.send(eanswer) nil end end
Add a callback which will be invoked upon an incoming file-transfer
block takes two arguments:
Bytestreams::IqSiFile in the Iq
You may then invoke accept or decline
# File lib/xmpp4r/bytestreams/helper/filetransfer.rb, line 172 def add_incoming_callback(priority = 0, ref = nil, &block) @incoming_cbs.add(priority, ref, block) end
Decline an incoming file-transfer, to be used in a block given to #add_incoming_callback
of file-transfer we want to decline
# File lib/xmpp4r/bytestreams/helper/filetransfer.rb, line 228 def decline(iq) answer = iq.answer(false) answer.type = :error error = answer.add(ErrorResponse.new('forbidden', 'Offer declined')) error.type = :cancel @stream.send(answer) 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 ServerError
to send the file to
File-transfer source, implementing the FileSource interface
or [nil] Optional file description
or [nil] Optional jid for components
or [Bytestreams::IBBInitiator] or [nil]
# File lib/xmpp4r/bytestreams/helper/filetransfer.rb, line 250 def offer(jid, source, desc=nil, from=nil) from = from || @my_jid || @stream.jid session_id = Jabber::IdGenerator.instance.generate_id offered_methods = {} if @allow_bytestreams offered_methods[Bytestreams::NS_BYTESTREAMS] = nil end if @allow_ibb offered_methods[Bytestreams::IBB::NS_IBB] = nil end iq = Iq.new(:set, jid) iq.from = from si = iq.add(Bytestreams::IqSi.new(session_id, Bytestreams::PROFILE_FILETRANSFER, source.mime)) file = si.add(Bytestreams::IqSiFile.new(source.filename, source.size)) file.hash = source.md5 file.date = source.date file.description = desc if desc file.add(Bytestreams::IqSiFileRange.new) if source.can_range? feature = si.add(REXML::Element.new('feature')) feature.add_namespace 'http://jabber.org/protocol/feature-neg' x = feature.add(Dataforms::XData.new(:form)) stream_method_field = x.add(Dataforms::XDataField.new('stream-method', :list_single)) stream_method_field.options = offered_methods begin stream_method = nil response = nil @stream.send_with_id(iq) do |r| response = r si = response.first_element('si') if si and si.feature and si.feature.x stream_method = si.feature.x.field('stream-method').values.first if si.file and si.file.range if source.can_range? source.seek(si.file.range.offset) if si.file.range.offset source.length = si.file.range.length if si.file.range.length else source.read(si.file.range.offset) end end end end rescue ServerError => e if e.error.code == 403 # Declined return false else raise e end end if stream_method == Bytestreams::NS_BYTESTREAMS and @allow_bytestreams Bytestreams::SOCKS5BytestreamsInitiator.new(@stream, session_id, from, jid) elsif stream_method == Bytestreams::IBB::NS_IBB and @allow_ibb Bytestreams::IBBInitiator.new(@stream, session_id, from, jid) else # Target responded with a stream_method we didn't offer eanswer = response.answer eanswer.type = :error eanswer.add ErrorResponse.new('bad-request') @stream.send(eanswer) nil end end