# class GxG::Communications::BridgeAdapterXMPP

XMPP

### Public Class Methods

abilities() click to toggle source

    # File lib/gxg/gxg_communications.rb, line 139
    def self.abilities()
        [:message, :file_transfer, :request]
    end

limits() click to toggle source

    # File lib/gxg/gxg_communications.rb, line 143
    def self.limits()
        {:message_size => 1048576, :file_transfer_size => nil, :request_size => 65536}
    end

new(the_process=nil, the_url=nil, options={:sasl => :digest, :digest => true}) click to toggle source
Calls superclass method `GxG::Communications::BridgeAdapter::new`

    # File lib/gxg/gxg_communications.rb, line 213
    def initialize(the_process=nil, the_url=nil, options={:sasl => :digest, :digest => true})
        super(the_process, the_url, options)
        unless @options[:sasl]
            @options = {:sasl => :digest, :digest => true}
        end
        #
        # Channels format: {<uuid> => {:channel => <XMPPConversation>, :entities => [<jid-string>, ...]}}
        @channels = {}
        # Keychain format: {:original => "", :current => ""}
        @keychain = {}
        # Messages format: {<uuid> => [<::GxG::Events::Message>, ...]}
        @messages = {}
        @messages_busy_semaphore = false
        @invitations = {}
        @file_transfers = {}
        #
        @connector = ::GxG::Networking::XmppClient.new({:notify => self})
        self.open
        #
        self
    end

protocol() click to toggle source

    # File lib/gxg/gxg_communications.rb, line 135
    def self.protocol()
        :xmpp
    end

templates() click to toggle source

    # File lib/gxg/gxg_communications.rb, line 147
    def self.templates()
        {}
    end

### Public Instance Methods

accept_file_transfer(the_transfer_uuid=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 402
    def accept_file_transfer(the_transfer_uuid=nil)
        result = false
        if ::GxG::valid_uuid?(the_transfer_uuid)
            found = nil
            @thread_safety.synchronize {
                found = @file_transfers[(the_transfer_uuid.to_sym)]
            }
            if found
                found[:transfer].accept()
                result = true
            end
        end
        result
    end

accept_invitation(the_invitation_uuid=nil) click to toggle source

Invitations

    # File lib/gxg/gxg_communications.rb, line 362
    def accept_invitation(the_invitation_uuid=nil)
        result = false
        if ::GxG::valid_uuid?(the_invitation_uuid)
            found = nil
            @thread_safety.synchronize {
                found = @invitations[(the_invitation_uuid.to_sym)]
            }
            if found
                found[:invitation].accept()
                result = true
            end
        end
        result
    end

all_messages(the_channel_uuid=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 748
    def all_messages(the_channel_uuid=nil)
        result = []
        if ::GxG::valid_uuid?(the_channel_uuid)
            @thread_safety.synchronize {
                if @messages[(the_channel_uuid.to_sym)].is_a?(::Array)
                    @messages[(the_channel_uuid.to_sym)].size.times do
                        result << decode_message(@messages[(the_channel_uuid.to_sym)].shift)
                    end
                end
            }
        end
        result
    end

associate(recipient=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 541
    def associate(recipient=nil)
        result = false
        if recipient.is_a?(::String)
            if recipient.valid_jid?
                @adapter.add_buddy(recipient)
                result = true
            end
        end
        result
    end

cancel_file_transfer(the_transfer_uuid=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 435
    def cancel_file_transfer(the_transfer_uuid=nil)
        result = false
        if ::GxG::valid_uuid?(the_transfer_uuid)
            found = nil
            @thread_safety.synchronize {
                found = @file_transfers[(the_transfer_uuid.to_sym)]
            }
            if found
                found[:transfer].cancel()
                if ::File.exists?(GxG::SERVER_PATHS[:temporary] + "/" + found[:transfer].reference.to_s)
                    ::GxG::VFS.rmdir("/System/Temporary/" + found[:transfer].reference.to_s)
                end
                result = true
            end
        end
        result
    end

channels() click to toggle source

Basic Channel `Support`

    # File lib/gxg/gxg_communications.rb, line 531
    def channels()
        result = {}
        @thread_safety.synchronize {
            @channels.each_pair do |the_uuid, the_record|
                result[(the_uuid)] = the_record[:title]
            end
        }
        result
    end

close_channel(the_channel_uuid=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 637
    def close_channel(the_channel_uuid=nil)
        if ::GxG::valid_uuid?(the_channel_uuid)
            the_channel = nil
            @thread_safety.synchronize {
                if @channels[(the_channel_uuid.to_sym)].is_a?(::Hash)
                    the_channel = @channels.delete(the_channel_uuid.to_sym)[:channel]
                end
            }
            if the_channel
                the_channel.leave("{ \"exit_channel\":\"#{the_channel.my_jid.to_s}\" }")
                true
            else
                false
            end
        else
            false
        end
    end

decline_file_transfer(the_transfer_uuid=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 417
    def decline_file_transfer(the_transfer_uuid=nil)
        result = false
        if ::GxG::valid_uuid?(the_transfer_uuid)
            found = nil
            @thread_safety.synchronize {
                found = @file_transfers[(the_transfer_uuid.to_sym)]
            }
            if found
                found[:transfer].decline()
                if ::File.exists?(GxG::SERVER_PATHS[:temporary] + "/" + found[:transfer].reference.to_s)
                    ::GxG::VFS.rmdir("/System/Temporary/" + found[:transfer].reference.to_s)
                end
                result = true
            end
        end
        result
    end

decline_invitation(the_invitation_uuid=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 377
    def decline_invitation(the_invitation_uuid=nil)
        result = false
        if ::GxG::valid_uuid?(the_invitation_uuid)
            found = nil
            @thread_safety.synchronize {
                found = @invitations[(the_invitation_uuid.to_sym)]
            }
            if found
                found[:invitation].decline()
                result = true
            end
        end
        result
    end

disassociate(recipient=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 552
    def disassociate(recipient=nil)
        result = false
        if recipient.is_a?(::String)
            if recipient.valid_jid?
                @adapter.remove_buddy(recipient)
                result = true
            end
        end
        result
    end

entities() click to toggle source

Entities

    # File lib/gxg/gxg_communications.rb, line 453
    def entities()
        result = []
        @connector.buddies().each do |record|
            new_record = {:id => "", :title => "Untitled", :groups => [], :status => :offline}
            new_record[:id] = record[:jid]
            new_record[:title] = record[:title]
            new_record[:groups] = record[:groups]
            case record[:status]
            when :chat, :normal, :available
                new_record[:status] = :online
            when :dnd
                new_record[:status] = :busy
            when :away
                new_record[:status] = :away
            when :xa
                new_record[:status] = :extended_away
            when :unavailable, :offline
                new_record[:status] = :offline
            when :error
                new_record[:status] = :error
            end
            result << new_record
        end
        result
    end

entity_status(the_address=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 479
    def entity_status(the_address=nil)
        result = :invalid_address
        if the_address.valid_jid?()
            result = :unknown_address
            self.entities.each do |the_record|
                if the_record[:id] == the_address
                    result = the_record[:status]
                    break
                end
            end
        end
        result
    end

file_transfers() click to toggle source

`File` Transfers

    # File lib/gxg/gxg_communications.rb, line 392
    def file_transfers()
        result = {}
        @thread_safety.synchronize {
            @file_transfers.each_pair do |the_uuid, the_record|
                result[(the_uuid)] = {:sender => the_record[:sender], :file => the_record[:file], :size => the_record[:size], :path => the_record[:path]}
            end
        }
        result
    end

get_message(the_channel_uuid=nil, the_message_uuid=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 762
    def get_message(the_channel_uuid=nil, the_message_uuid=nil)
        result = nil
        if ::GxG::valid_uuid?(the_channel_uuid) && the_message_uuid
            @thread_safety.synchronize {
                if @messages[(the_channel_uuid.to_sym)].is_a?(::Array)
                    @messages[(the_channel_uuid.to_sym)].each_with_index do |the_message, the_index|
                        if the_message_uuid == the_message.id()
                            result = @messages[(the_channel_uuid.to_sym)].delete_at(the_index)
                            break
                        end
                    end
                end
            }
            if result
                result = decode_message(result)
            end
        end
        result
    end

get_messages_by_context(the_channel_uuid=nil, the_context_uuid=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 782
    def get_messages_by_context(the_channel_uuid=nil, the_context_uuid=nil)
        result = []
        if ::GxG::valid_uuid?(the_channel_uuid) && the_context_uuid
            @thread_safety.synchronize {
                if @messages[(the_channel_uuid.to_sym)].is_a?(::Array)
                    @messages[(the_channel_uuid.to_sym)].each_with_index do |the_message, the_index|
                        if the_context_uuid.to_s.to_sym == the_message[:context].to_s.to_sym
                            result << @messages[(the_channel_uuid.to_sym)].delete_at(the_index)
                        end
                    end
                end
            }
            if result
                result = decode_message(result)
            end
        end
        result
    end

get_messages_by_sender(the_channel_uuid=nil, the_sender=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 801
    def get_messages_by_sender(the_channel_uuid=nil, the_sender=nil)
        result = []
        if ::GxG::valid_uuid?(the_channel_uuid) && the_sender
            @thread_safety.synchronize {
                if @messages[(the_channel_uuid.to_sym)].is_a?(::Array)
                    @messages[(the_channel_uuid.to_sym)].each_with_index do |the_message, the_index|
                        if the_sender.to_s == the_message[:sender].to_s
                            result << @messages[(the_channel_uuid.to_sym)].delete_at(the_index)
                        end
                    end
                end
            }
            if result
                result = decode_message(result)
            end
        end
        result
    end

join_channel(channel_jid=nil, password=nil, options={}) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 609
    def join_channel(channel_jid=nil, password=nil, options={})
        result = nil
        if channel_jid.is_a?(::String)
            if channel_jid.valid_jid? && options.is_any?(::Hash, ::GxG::Database::PersistedHash)
                if @connector.join_conversation(channel_jid, password, options) == true
                    existing_channels = self.channels()
                    found = nil
                    @thread_safety.synchronize {
                        @connector.conversations.each do |channel_object|
                            if existing_channels.keys.include?(channel_object.uuid)
                                next
                            else
                                found = channel_object
                                @channels[(channel_object.uuid)] = {:channel => found, :title => found.title()}
                                break
                            end
                        end
                    }
                    if found
                        result = channel_object.uuid
                    end
                    #
                end
            end
        end
        result
    end

my_id() click to toggle source

    # File lib/gxg/gxg_communications.rb, line 235
    def my_id()
        if @connector
            @connector.my_jid.to_s
        else
            nil
        end
    end

next_message(the_channel_uuid=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 733
    def next_message(the_channel_uuid=nil)
        result = nil
        if ::GxG::valid_uuid?(the_channel_uuid)
            @thread_safety.synchronize {
                if @messages[(the_channel_uuid.to_sym)].is_a?(::Array)
                    result = @messages[(the_channel_uuid.to_sym)].shift
                end
            }
            if result
                result = decode_message(result)
            end
        end
        result
    end

notify(details=nil) click to toggle source

Information Refresh

    # File lib/gxg/gxg_communications.rb, line 243
    def notify(details=nil)
        if details.is_a?(::Hash)
            if @process
                if @process.respond_to?(:dispatcher) && @process.respond_to?(:respond_to_event?)
                    case details[:event]
                    when :new_invitation
                        active_list = {}
                        @connector.invitations.each do |the_invitation|
                            active_list[(the_invitation.uuid)] = {:invitation => the_invitation, :title => the_invitation[:title].to_s, :sender => the_invitation[:sender].to_s, :type => the_invitation[:type].to_s}
                        end
                        @thread_safety.synchronize { @invitations = active_list }
                        #
                        if @process.respond_to_event?(:new_invitation)
                            @process.dispatcher.post_event(:communications) do
                                @process.call_event({:new_invitation => {:invitation => details[:invitation], :sender => details[:sender], :at => ::DateTime.now}})
                            end
                        end
                    when :new_file_transfer
                        # First, update Bridge Manifest
                        active_list = {}
                        @connector.file_transfers.each do |the_transfer|
                            reference = the_transfer.reference
                            file_name = the_transfer.file_details[:filename]
                            file_size = the_transfer.file_details[:size]
                            sender = the_transfer.info[:sender]
                            # set download path
                            unless ::File.exists?(GxG::SERVER_PATHS[:temporary] + "/" + reference.to_s)
                                ::FileUtils.mkpath(GxG::SERVER_PATHS[:temporary] + "/" + reference.to_s)
                            end
                            the_transfer.download_directory = (GxG::SERVER_PATHS[:temporary] + "/" + reference.to_s)
                            # store entry
                            active_list[(reference)] = {:transfer => the_transfer, :sender => sender, :file => file_name, :size => file_size, :path => ("/System/Temporary/" + reference.to_s + "/" + file_name)}
                        end
                        @thread_safety.synchronize { @file_transfers = active_list }
                        if @process.respond_to_event?(:new_file_transfer)
                            @process.dispatcher.post_event(:communications) do
                                @process.call_event({:new_file_transfer => {:transfer => details[:transfer], :sender => details[:sender], :at => ::DateTime.now}})
                            end
                        end
                    when :file_transfer_complete, :file_transfer_cancelled, :file_transfer_error
                        notification = {}
                        notification[(details[:event])] = {:transfer => details[:transfer], :at => ::DateTime.now}
                        #
                        active_list = {}
                        @connector.file_transfers.each do |the_transfer|
                            reference = the_transfer.reference
                            file_name = the_transfer.file_details[:filename]
                            file_size = the_transfer.file_details[:size]
                            sender = the_transfer.info[:sender]
                            # store entry
                            active_list[(reference)] = {:transfer => the_transfer, :sender => sender, :file => file_name, :size => file_size, :path => ("/System/Temporary/" + reference.to_s + "/" + file_name)}
                        end
                        @thread_safety.synchronize { @file_transfers = active_list }
                        #
                        if @process.respond_to_event?(details[:event])
                            @process.call_event(notification)
                        end
                    when :file_transfer_progress
                        if @process.respond_to_event?(:file_transfer_progress)
                            @process.call_event({:file_transfer_progress => {:transfer => details[:transfer], :progress => details[:progress], :at => ::DateTime.now}})
                        end
                    when :new_message, :new_private_message, :new_announcement, :new_error
                        # ### unless channel exists - add it
                        the_channel = @thread_safety.synchronize { @channels[(details[:conversation].to_sym)] }
                        unless the_channel
                            found = nil
                            @connector.conversations.each do |channel_object|
                                if details[:conversation].to_sym == channel_object.uuid
                                    found = channel_object
                                    break
                                end
                            end
                            if found
                                @thread_safety.synchronize { @channels[(details[:conversation].to_sym)] = {:channel => found, :title => found.title()}}
                                the_channel = found
                            end
                        end
                        # ### get messages
                        if the_channel
                            unless @thread_safety.synchronize { @messages[(the_channel.uuid)].is_a?(::Array) }
                                @thread_safety.synchronize { @messages[(the_channel.uuid)] = [] }
                            end
                            the_channel.process_received() do |the_message|
                                # Note: this will pick up other messages from the adapter now, but it is ok - a notification will be generated on EACH message.
                                the_message[:channel] = the_channel.uuid
                                @thread_safety.synchronize { @messages[(the_channel.uuid)] << the_message }
                            end
                        end
                        # Review : differentiate beteween private and open messages? How to do?
                        if @process.respond_to_event?(:new_message)
                            notification = {}
                            notification[:new_message] = {:channel => details[:conversation], :message => details[:message], :sender => details[:sender].to_s, :at => ::DateTime.now}
                            #
                            @process.dispatcher.post_event(:communications) do
                                @process.call_event(notification)
                            end
                        end
                        #
                    end
                end
            end
        end
    end

open_channel(with_jid=nil, password=nil, configuration={}) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 563
    def open_channel(with_jid=nil, password=nil, configuration={})
        result = nil
        if with_jid.is_a?(::String)
            if with_jid.valid_jid? && configuration.is_any?(::Hash, ::GxG::Database::PersistedHash)
                # If channel already exists and they attempt a repeat opening action:
                @thread_safety.synchronize {
                    @channels.each_pair do |the_uuid, the_record|
                        if with_jid.to_s == the_record.jid.to_s
                            result = the_record.uuid
                            break
                        end
                    end
                }
                unless result
                    # Channel needs to be constructed:
                    if @connector.create_conversation(with_jid, password, configuration) == true
                        existing_channels = self.channels()
                        found = nil
                        @thread_safety.synchronize {
                            @connector.conversations.each do |channel_object|
                                if existing_channels.keys.include?(channel_object.uuid)
                                    next
                                else
                                    found = channel_object
                                    @channels[(channel_object.uuid)] = {:channel => found, :title => found.title()}
                                    break
                                end
                            end
                        }
                        if found
                            result = channel_object.uuid
                            if password
                                if channel_object[:type] == :chat
                                    # on :groupchat password is only used for access, not encryption.
                                    self.set_key(with_jid, password)
                                end
                            end
                        end
                        #
                    end
                end
            end
        end
        result
    end

send_message(the_channel_uuid=nil, message=nil, recipient=nil, use_fallback_key=false) click to toggle source

Messages

    # File lib/gxg/gxg_communications.rb, line 656
    def send_message(the_channel_uuid=nil, message=nil, recipient=nil, use_fallback_key=false)
        result = false
        #
        if ::GxG::valid_uuid?(the_channel_uuid)
            the_channel = nil
            @thread_safety.synchronize {
                if @channels[(the_channel_uuid.to_sym)].is_a?(::Hash)
                    the_channel = @channels[(the_channel_uuid.to_sym)][:channel]
                end
            }
            if the_channel
                if message.is_any?(::String, ::Hash, ::GxG::Database::PersistedHash, ::GxG::Events::Message)
                    if message.is_a?(::String)
                        message = new_message({:sender => @connector.my_jid.to_s, :body => {:message => message}.gxg_export.to_json})
                    end
                    if message.is_a?(::Hash)
                        message = new_message({:sender => @connector.my_jid.to_s, :body => message.gxg_export.to_json})
                    end
                    if message.is_a?(::GxG::Database::PersistedHash)
                        message = new_message({:sender => @connector.my_jid.to_s, :body => message.sync_export.gxg_export.to_json})
                    end
                    if message.is_a?(::GxG::Events::Message)
                        if message[:body].is_a?(::Hash)
                            message[:body] = message[:body].gxg_export.to_json
                        end
                        if message[:body].is_a?(::GxG::Database::PersistedHash)
                            message[:body] = message[:body].sync_export.gxg_export.to_json
                        end
                    end
                    #
                    if message.is_a?(::GxG::Events::Message)
                        # Reset :sender if a ruby object is the current setting. (bridge addressing translation)
                        unless message[:sender].is_a?(::String)
                            message[:sender] = @connector.my_jid.to_s
                        end
                        # encrypt body?
                        key = nil
                        if recipient.is_a?(::String)
                            intended_recipient = recipient
                        else
                            intended_recipient = the_channel.jid()
                        end
                        @thread_safety.synchronize {
                            if @keychain[(intended_recipient.to_s)]
                                if use_fallback_key == true
                                    key = @keychain[(intended_recipient.to_s)][:original]
                                else
                                    key = @keychain[(intended_recipient.to_s)][:current]
                                end
                            end
                        }
                        if key.is_a?(::String)
                            message[:body] = message[:body].encrypt(key).encode64
                        else
                            message[:body] = message[:body].encode64
                        end
                        # send message
                        result = the_channel.say_something(message, recipient)
                        # Review : use this next little bit of code in the request/reply code:
                        # if result == true
                        #     message.succeed(message)
                        # else
                        #     message.fail(message)
                        # end
                    end
                else
                    raise ArgumentError, "You MUST provide the message as a String, Hash, GxG::Database::PersistedHash, or GxG::Events::Message; NOT #{message.class} ."
                end
            else
                raise ArgumentError, "Invalid Channel selector, the Channel was not found: #{the_channel.inspect} . Try opening the channel first."
            end
        else
            raise ArgumentError, "Invalid Channel selector: #{the_channel.inspect} . You MUST provide a valid UUID."
        end
        result
    end

set_key(recipient=nil, password=nil) click to toggle source

Keychain

    # File lib/gxg/gxg_communications.rb, line 347
    def set_key(recipient=nil, password=nil)
        result = false
        if recipient.to_s.valid_jid? && password.is_a?(::String)
            @thread_safety.synchronize {
                if @keychain[(recipient.to_s)]
                    @keychain[(recipient.to_s)][:current] = password
                else
                    @keychain[(recipient.to_s)] = {:original => password, :current => password}
                end
                result = true
            }
        end
        result
    end

status() click to toggle source

    # File lib/gxg/gxg_communications.rb, line 493
    def status()
        result = :offline
        case @adapter.get_status
        when :chat, :normal, :available
            result = :online
        when :dnd
            result = :busy
        when :away
            result = :away
        when :xa
            result = :extended_away
        when :unavailable, :offline
            result = :offline
        when :error
            result = :error
        end
        result
    end

status=(the_status=nil, message=nil) click to toggle source

    # File lib/gxg/gxg_communications.rb, line 512
    def status=(the_status=nil, message=nil)
        result = :available
        case the_status
        when :online
            result = :available
        when :busy
            result = :dnd
        when :away
            result = :away
        when :extended_away
            result = :xa
        when :offline
            result = :unavailable
        when :error
            result = :error
        end
        @adapter.set_status(result, message)
    end