class GxG::Networking::XmppAgentFileTransfer

File Transfer

Public Class Methods

new(manager, direction, details, parent) click to toggle source

Review : totally rewrite this

# File lib/gxg/net_clients.rb, line 2538
def initialize(manager, direction, details, parent)
  @reference = ::GxG::uuid_generate.to_sym
  @session_id = nil
  @pending = true
  @inprogress = false
  @percent_completed = 0
  @status_thread_safety = ::Mutex.new
  @manager = manager
  @direction = direction
  @info = {:reference => @reference}
  @bytestream = nil
  @file_details = nil
  @cancel = false
  the_value = ::GxG::SYSTEM.maximum_buffer_size.get_at_path("/:ipv4/:tcp/:write/:valid")
  if the_value.is_a?(::Range)
    @valid_write_buffer_sizes = the_value
  else
    @valid_write_buffer_sizes = (4096..65536)
  end
  @buffer_size = 4096
  @client = parent
  if @direction == :download
    @invitation = details[:iq]
    @sender = details[:iq].from
    @info[:sender] = @sender.to_s
    @info[:recipient] = @client.jid.to_s
    @file_details = {:filename => details[:file_info].fname, :path => (details[:download_directory] + "/" + details[:file_info].fname), :size => details[:file_info].size, :md5 => details[:file_info].hash, :description => details[:file_info].description}
    @download_directory = details[:download_directory]
    @info[:details] = @file_details
    @recipient = nil
    #
    #
  end
  if @direction == :upload
    @info[:sender] = @client.jid.to_s
    @info[:recipient] = details[:jid].to_s
    @recipient = ::Jabber::JID.new(details[:jid])
    @file_details = {:filename => ::File.basename(details[:path]), :path => details[:path], :size => ::File.size(details[:path]), :md5 => ::Digest::MD5.file(details[:path]), :description => details[:description]}
    @info[:details] = @file_details
    @invitation = nil
    @download_directory = nil
  end
  self
end

Public Instance Methods

accept() click to toggle source
# File lib/gxg/net_clients.rb, line 2651
def accept()
  if @client
    if @status_thread_safety.synchronize { @pending == true && @direction == :download }
      Thread.new {
        begin
          bytes_received = 0
          total_size = @file_details[:size]
          @bytestream = @manager.accept(@invitation)
          if @bytestream
            @bytestream.accept()
            @status_thread_safety.synchronize {
              @pending = false
              @inprogress = true
              @session_id = @bytestream.session_id.to_s
            }
            #
            cancel_callback = Proc.new do |the_iq|
              if the_iq.type == :error
                the_error = ::Jabber::ServerError.new(the_iq.error)
                if @invitation.id.to_s == the_iq.id.to_s
                  the_transfer = @client.find_file_transfer(@reference)
                  if the_transfer
                    the_transfer.cancel
                  end
                end
                log_error({:error => the_error, :parameters => {:iq => the_iq}})
              end
            end
            @client.set_xfr_callback(@session_id,&cancel_callback)
            #
            the_file = ::File.open(@file_details[:path], "w+b",0664)
            the_file.pos = 0
            buffer = @bytestream.read()
            while buffer && @status_thread_safety.synchronize { @cancel == false } do
              the_file.write(buffer.to_s)
              bytes_received += buffer.to_s.size
              @status_thread_safety.synchronize { @percent_completed = ((bytes_received.to_f / total_size.to_f) * 100.0).to_i }
              @client.notify({:event => :file_transfer_progress, :transfer => @reference, :progress => @status_thread_safety.synchronize { @percent_completed }})
              buffer = @bytestream.read()
            end
            the_file.close
            @bytestream.close
            @status_thread_safety.synchronize {
              @inprogress = false
              @pending = false
            }
            the_path = @file_details[:path]
            if @status_thread_safety.synchronize { @cancel == true }
              ::File.delete(the_path)
              @client.notify({:event => :file_transfer_cancelled, :transfer => @reference})
            else
              if @file_details[:md5]
                unless @file_details[:md5] == ::Digest::MD5.file(the_path)
                  raise Exception, "MD5 Checksum Failure - file corruption on file: #{the_path}"
                end
                @client.notify({:event => :file_transfer_complete, :transfer => @reference})
              end
            end
          else
            @status_thread_safety.synchronize {
              @inprogress = false
              @pending = false
            }
            the_file = nil
            raise Exception, "Failed to negotiate a transfer method."
          end
        rescue Exception => the_error
          @status_thread_safety.synchronize {
            @inprogress = false
            @pending = false
          }
          if the_file
            the_file.close
          end
          if @bytestream
            @bytestream.close
          end
          log_error({:error => the_error, :parameters => {}})
          @client.notify({:event => :file_transfer_error, :transfer => @reference})
        end
        if @session_id
          @client.clear_xfr_callback(@session_id)
        end
        @client.refresh_file_transfers
      }
    end
    true
  else
    false
  end
end
buffer_size() click to toggle source
# File lib/gxg/net_clients.rb, line 2603
def buffer_size()
  @status_thread_safety.synchronize { @buffer_size }
end
buffer_size=(the_size=4096) click to toggle source
# File lib/gxg/net_clients.rb, line 2607
def buffer_size=(the_size=4096)
  begin
    unless the_size.is_a?(::Integer)
      raise ArgumentError, "You MUST provide an Integer to set buffer size."
    end
    if @status_thread_safety.synchronize { @direction == :upload }
      unless @valid_write_buffer_sizes.include?(the_size)
        raise ArgumentError, "You MUST provide an Integer between #{@valid_read_buffer_sizes.first} and #{@valid_read_buffer_sizes.last}."
        @status_thread_safety.synchronize { @buffer_size = the_size }
      end
    else
      log_warn("Buffer size is only set on upload file transfers.")
    end
    the_size
  rescue Exception => the_error
    log_error({:error => the_error, :parameters => {}})
    nil
  end
end
cancel() click to toggle source
# File lib/gxg/net_clients.rb, line 2631
def cancel()
  if @client
    begin
      if @status_thread_safety.synchronize { @pending == true }
        @status_thread_safety.synchronize { @pending = false }
      end
      if @status_thread_safety.synchronize { @inprogress == true }
        @status_thread_safety.synchronize { @inprogress = false }
      end
      @status_thread_safety.synchronize { @cancel = true }
      @client.refresh_file_transfers
      true
    rescue Exception => the_error
      log_error({:error => the_error, :parameters => {}})
    end
  else
    false
  end
end
cancelled?() click to toggle source
# File lib/gxg/net_clients.rb, line 2627
def cancelled?()
  @status_thread_safety.synchronize { @cancel }
end
decline() click to toggle source
# File lib/gxg/net_clients.rb, line 2743
def decline()
  if @client
    if @status_thread_safety.synchronize { @pending == true && @direction == :download }
      begin
        @manager.decline(@invitation)
        @status_thread_safety.synchronize { @pending = false }
      rescue Exception => the_error
        log_error({:error => the_error, :parameters => {}})
      end
    end
    true
  else
    false
  end
end
download_directory() click to toggle source
# File lib/gxg/net_clients.rb, line 2771
def download_directory()
  @status_thread_safety.synchronize { @download_directory.clone }
end
download_directory=(the_path=nil) click to toggle source
# File lib/gxg/net_clients.rb, line 2775
def download_directory=(the_path=nil)
  begin
    unless the_path.is_a?(::String)
      raise ArgumentError, "You MUST supply a String as valid directory path."
    end
    the_path = ::File.expand_path(the_path)
    unless the_path.valid_path?()
      raise ArgumentError, "You MUST supply a valid directory path."
    end
    unless ::File.exist?(the_path)
      raise ArgumentError, "You MUST supply a valid directory path - Directory does not exist."
    end
    @status_thread_safety.synchronize {
      unless @inprogress == true
        if @direction == :download
          @download_directory = ::File.expand_path(the_path)
          @file_details[:path] = (@download_directory + "/" + @file_details[:filename])
        end
      end
      }
    rescue Exception => the_error
      log_error({:error => the_error, :parameters => {}})
    end
end
file_details() click to toggle source
# File lib/gxg/net_clients.rb, line 2767
def file_details()
  @status_thread_safety.synchronize { @file_details.clone }
end
in_progress?() click to toggle source
# File lib/gxg/net_clients.rb, line 2759
def in_progress?()
  @status_thread_safety.synchronize { @inprogress }
end
info() click to toggle source
# File lib/gxg/net_clients.rb, line 2587
def info()
  @info.clone
end
inspect() click to toggle source
# File lib/gxg/net_clients.rb, line 2583
def inspect()
  "<Transfer: #{@direction.inspect} - #{@reference}, with Session ID: #{@session_id} for File #{@file_details[:filename]}>"
end
pending?() click to toggle source
# File lib/gxg/net_clients.rb, line 2599
def pending?()
  @status_thread_safety.synchronize { @pending }
end
percent_completed() click to toggle source
# File lib/gxg/net_clients.rb, line 2763
def percent_completed()
  @status_thread_safety.synchronize { @percent_completed.clone }
end
reference() click to toggle source
# File lib/gxg/net_clients.rb, line 2591
def reference()
  @reference.clone
end
session_id() click to toggle source
# File lib/gxg/net_clients.rb, line 2595
def session_id()
  @session_id.clone
end
upload_file(proxy=nil) click to toggle source
# File lib/gxg/net_clients.rb, line 2800
def upload_file(proxy=nil)
  if @client
    if @direction == :upload
      unless @status_thread_safety.synchronize { @inprogress == true }
        Thread.new {
          begin
            bytes_sent = 0
            buffer_size = @status_thread_safety.synchronize { @buffer_size }
            the_file_source = ::Jabber::FileTransfer::FileSource.new(@file_details[:path])
            the_file_source.length = buffer_size
            total_size = the_file_source.size
            @bytestream = nil
            response = @manager.offer(@recipient,the_file_source,@file_details[:description],nil,@reference.to_s)
            if response.is_a?(::Hash)
              @bytestream = response[:bytestream]
              the_iq_id = response[:iq_id]
              if @bytestream
                if @bytestream.is_a?(::Jabber::Bytestreams::SOCKS5BytestreamsInitiator)
                  if proxy.is_any?(::String, ::Jabber::JID)
                    @bytestream.add_streamhost(proxy)
                  end
                end
                if @bytestream.is_a?(::Jabber::Bytestreams::IBBInitiator)
                  @bytestream.block_size = buffer_size
                end
                #
                @status_thread_safety.synchronize {
                  @pending = false
                  @inprogress = true
                }
                open_response = @bytestream.open()
                if open_response
                  puts "Got: (open_response) - #{open_response.inspect}"
                  if open_response.type == :result
                    #
                    @session_id = @bytestream.session_id.to_s
                    #
                    cancel_callback = Proc.new do |the_iq|
                      if the_iq.type == :error
                        the_error = ::Jabber::ServerError.new(the_iq.error)
                        if the_iq_id.to_s == the_iq.id.to_s
                          the_transfer = @client.find_file_transfer(@reference)
                          if the_transfer
                            the_transfer.cancel
                          end
                        end
                        log_error({:error => the_error, :parameters => {:iq => the_iq}})
                      end
                    end
                    @client.set_xfr_callback(@session_id,&cancel_callback)
                    #
                    ::GxG.apportioned_ranges(total_size,buffer_size).each do |the_range|
                      if @status_thread_safety.synchronize { @cancel == true }
                        the_file_source.close
                        break
                      end
                      the_file_source.seek(the_range.first)
                      bytes_sent += @bytestream.write(the_file_source.read(the_range.size)).to_i
                      @status_thread_safety.synchronize { @percent_completed = ((bytes_sent.to_f / total_size.to_f) * 100.0).to_i }
                      #
                    end
                    @bytestream.flush
                    @bytestream.close
                    @status_thread_safety.synchronize { @inprogress = false }
                  else
                    # Error?
                    @status_thread_safety.synchronize {
                      @pending = false
                      @inprogress = false
                    }
                  end
                else
                  raise Exception, "Failed to open the stream."
                end
              end
            else
              @status_thread_safety.synchronize {
                @pending = false
                @inprogress = false
              }
            end
          rescue Exception => the_error
            the_file_source.close
            @bytestream.close
            @status_thread_safety.synchronize { @inprogress = false }
            log_error({:error => the_error, :parameters => {:proxy => proxy}})
          end
          @client.clear_xfr_callback(@reference.to_s)
          if @session_id
            @client.clear_xfr_callback(@session_id)
          end
          @client.refresh_file_transfers
        }
      end
    end
  end
end