# 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