Class: Jabber::FileTransfer::Helper

Inherits:
Object
  • Object
show all
Defined in:
lib/xmpp4r/bytestreams/helper/filetransfer.rb

Overview

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stream) ⇒ Helper

Create a new FileTransfer instance



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/xmpp4r/bytestreams/helper/filetransfer.rb', line 140

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

Instance Attribute Details

#allow_bytestreamsObject

Set this to false if you don't want to use SOCKS5Bytestreams



133
134
135
# File 'lib/xmpp4r/bytestreams/helper/filetransfer.rb', line 133

def allow_bytestreams
  @allow_bytestreams
end

#allow_ibbObject

Set this to false if you don't want to use IBB



136
137
138
# File 'lib/xmpp4r/bytestreams/helper/filetransfer.rb', line 136

def allow_ibb
  @allow_ibb
end

#my_jidObject

Set this if you want to use this helper in a Component



130
131
132
# File 'lib/xmpp4r/bytestreams/helper/filetransfer.rb', line 130

def my_jid
  @my_jid
end

Instance Method Details

#accept(iq, offset = nil, length = nil) ⇒ Object

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



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/xmpp4r/bytestreams/helper/filetransfer.rb', line 187

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::IqQueryBytestreams::NS_BYTESTREAMS) and @allow_bytestreams
    stream_method.values = [Bytestreams::IqQueryBytestreams::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(Error.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_incoming_callback(priority = 0, ref = nil, &block) ⇒ Object

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

block takes two arguments:

  • Iq

  • Bytestreams::IqSiFile in the Iq

You may then invoke accept or decline



173
174
175
# File 'lib/xmpp4r/bytestreams/helper/filetransfer.rb', line 173

def add_incoming_callback(priority = 0, ref = nil, &block)
  @incoming_cbs.add(priority, ref, block)
end

#decline(iq) ⇒ Object

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



229
230
231
232
233
234
235
# File 'lib/xmpp4r/bytestreams/helper/filetransfer.rb', line 229

def decline(iq)
  answer = iq.answer(false)
  answer.type = :error
  error = answer.add(Error.new('forbidden', 'Offer declined'))
  error.type = :cancel
  @stream.send(answer)
end

#offer(jid, source, desc = nil, from = nil) ⇒ Object

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]



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/xmpp4r/bytestreams/helper/filetransfer.rb', line 251

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::IqQueryBytestreams::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::IqSi::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) { |r|
      response = r
      si = response.first_element('si')
      if response.type == :result and 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
      true
    }
  rescue ErrorException => e
    if e.error.code == 403  # Declined
      return false
    else
      raise e
    end
  end

  if stream_method == Bytestreams::IqQueryBytestreams::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 Error::new('bad-request')
    @stream.send(eanswer)
    nil
  end
end