class Jabber::Client

The client class provides everything needed to build a basic XMPP Client.

If you want your connection to survive disconnects and timeouts, catch exception in Jabber::Stream#on_exception and re-call #connect and #auth. Don't forget to re-send initial Presence and everything else you need to setup your session.

Attributes

jid[R]

The client's JID

Public Class Methods

new(jid) click to toggle source

Create a new Client.

Remember to always put a resource in your JID unless the server can do SASL.

Calls superclass method
# File lib/xmpp4r/client.rb, line 27
def initialize(jid)
  super()
  @jid = (jid.kind_of?(JID) ? jid : JID.new(jid.to_s))
end

Public Instance Methods

auth(password) click to toggle source

Authenticate with the server

Throws ClientAuthenticationFailure

Authentication mechanisms are used in the following preference:

  • SASL DIGEST-MD5

  • SASL PLAIN

  • Non-SASL digest

password
String
# File lib/xmpp4r/client.rb, line 107
def auth(password)
  begin
    if @stream_mechanisms.include? 'DIGEST-MD5'
      auth_sasl SASL.new(self, 'DIGEST-MD5'), password
    elsif @stream_mechanisms.include? 'PLAIN'
      auth_sasl SASL.new(self, 'PLAIN'), password
    else
      auth_nonsasl(password)
    end
  rescue
    Jabber::debuglog("#{$!.class}: #{$!}\n#{$!.backtrace.join("\n")}")
    raise ClientAuthenticationFailure.new, $!.to_s
  end
end
auth_anonymous() click to toggle source

See #auth_anonymous_sasl

# File lib/xmpp4r/client.rb, line 196
def auth_anonymous
  auth_anonymous_sasl
end
auth_anonymous_sasl() click to toggle source

Shortcut for anonymous connection to server

Throws ClientAuthenticationFailure

# File lib/xmpp4r/client.rb, line 205
def auth_anonymous_sasl
  if self.supports_anonymous?
    begin
      auth_sasl SASL.new(self, 'ANONYMOUS'), ""
    rescue
      Jabber::debuglog("#{$!.class}: #{$!}\n#{$!.backtrace.join("\n")}")
      raise ClientAuthenticationFailure, $!.to_s
    end
  else
    raise ClientAuthenticationFailure, 'Anonymous authentication unsupported'
  end
end
auth_nonsasl(password, digest=true) click to toggle source

Send auth with given password and wait for result (non-SASL)

Throws ServerError

password
String

the password

digest
Boolean

use Digest authentication

# File lib/xmpp4r/client.rb, line 234
def auth_nonsasl(password, digest=true)
  authset = nil
  if digest
    authset = Iq.new_authset_digest(@jid, @streamid.to_s, password)
  else
    authset = Iq.new_authset(@jid, password)
  end
  send_with_id(authset)
  $defout.flush

  true
end
auth_sasl(sasl, password) click to toggle source

Use a SASL authentication mechanism and bind to a resource

If there was no resource given in the jid, the jid/resource generated by the server will be accepted.

This method should not be used directly. Instead, #auth may look for the best mechanism suitable.

sasl

Descendant of [Jabber::SASL::Base]

password
String
# File lib/xmpp4r/client.rb, line 170
def auth_sasl(sasl, password)
  sasl.auth(password)

  # Restart stream after SASL auth
  stop
  start
  # And wait for features - again
  @features_sem.wait

  # Resource binding (RFC3920 - 7)
  if @stream_features.has_key? 'bind'
    @jid = bind(@jid.resource)
  end

  # Session starting
  if @stream_features.has_key? 'session'
    iq = Iq.new(:set)
    session = iq.add REXML::Element.new('session')
    session.add_namespace @stream_features['session']

    send_with_id(iq)
  end
end
bind(desired_resource=nil) click to toggle source

Resource binding (RFC3920bis-06 - section 8.)

XMPP allows to bind to multiple resources

# File lib/xmpp4r/client.rb, line 126
def bind(desired_resource=nil)
  iq = Iq.new(:set)
  bind = iq.add REXML::Element.new('bind')
  bind.add_namespace @stream_features['bind']
  if desired_resource
    resource = bind.add REXML::Element.new('resource')
    resource.text = desired_resource
  end

  jid = nil
  send_with_id(iq) do |reply|
    reply_bind = reply.first_element('bind')
    if reply_bind
      reported_jid = reply_bind.first_element('jid')
      if reported_jid and reported_jid.text
        jid = JID.new(reported_jid.text)
      end
    end
  end
  jid
end
close() click to toggle source

Close the connection, sends </stream:stream> tag first

Calls superclass method
# File lib/xmpp4r/client.rb, line 77
def close
  if @status == CONNECTED
    send("</stream:stream>")
  end
  super
end
connect(host = nil, port = 5222) click to toggle source

connect to the server (chaining-friendly)

If you omit the optional host argument SRV records for your jid will be resolved. If none works, fallback is connecting to the domain part of the jid.

host
String

Optional c2s host, will be extracted from jid if nil

use_ssl
Boolean

Optional. Use (old, deprecated) SSL when connecting.

return

self

Calls superclass method
# File lib/xmpp4r/client.rb, line 42
def connect(host = nil, port = 5222)
  if host.nil?
    begin
      srv = []
      Resolv::DNS.open { |dns|
        # If ruby version is too old and SRV is unknown, this will raise a NameError
        # which is caught below
        Jabber::debuglog("RESOLVING:\n_xmpp-client._tcp.#{@jid.domain} (SRV)")
        srv = dns.getresources("_xmpp-client._tcp.#{@jid.domain}", Resolv::DNS::Resource::IN::SRV)
      }
      # Sort SRV records: lowest priority first, highest weight first
      srv.sort! { |a,b| (a.priority != b.priority) ? (a.priority <=> b.priority) : (b.weight <=> a.weight) }

      srv.each { |record|
        begin
          connect(record.target.to_s, record.port)
          # Success
          return self
        rescue SocketError, Errno::ECONNREFUSED
          # Try next SRV record
        end
      }
    rescue NameError
      Jabber::debuglog "Resolv::DNS does not support SRV records. Please upgrade to ruby-1.8.3 or later!"
    end
    # Fallback to normal connect method
  end

  super(host.nil? ? jid.domain : host, port)
  self
end
password=(new_password) click to toggle source

Change the client's password

Threading is suggested, as this code waits for an answer.

Raises an exception upon error response (ServerError from Jabber::Stream#send_with_id).

new_password
String

New password

# File lib/xmpp4r/client.rb, line 334
def password=(new_password)
  iq = Iq.new_query(:set, @jid.domain)
  iq.query.add_namespace('jabber:iq:register')
  iq.query.add(REXML::Element.new('username')).text = @jid.node
  iq.query.add(REXML::Element.new('password')).text = new_password

  err = nil
  send_with_id(iq)
end
register(password, fields={}) click to toggle source

Register a new user account (may be used instead of #auth)

This method may raise ServerError if the registration was not successful.

password

String

fields

{String=>String} additional registration information

XEP-0077 Defines the following fields for registration information: www.xmpp.org/extensions/xep-0077.html

'username' => 'Account name associated with the user' 'nick' => 'Familiar name of the user' 'password' => 'Password or secret for the user' 'name' => 'Full name of the user' 'first' => 'First name or given name of the user' 'last' => 'Last name, surname, or family name of the user' 'email' => 'Email address of the user' 'address' => 'Street portion of a physical or mailing address' 'city' => 'Locality portion of a physical or mailing address' 'state' => 'Region portion of a physical or mailing address' 'zip' => 'Postal code portion of a physical or mailing address' 'phone' => 'Telephone number of the user' 'url' => 'URL to web page describing the user' 'date' => 'Some date (e.g., birth date, hire date, sign-up date)'

# File lib/xmpp4r/client.rb, line 303
def register(password, fields={})
  reg = Iq.new_register(jid.node, password)
  reg.to = jid.domain
  fields.each { |name,value|
    reg.query.add(REXML::Element.new(name)).text = value
  }

  send_with_id(reg)
end
register_info() click to toggle source

Get instructions and available fields for registration

return
instructions, fields

Where instructions is a String and fields is an Array of Strings

# File lib/xmpp4r/client.rb, line 250
def register_info
  instructions = nil
  fields = []

  reg = Iq.new_registerget
  reg.to = jid.domain
  send_with_id(reg) do |answer|
    if answer.query
      answer.query.each_element { |e|
        if e.namespace == 'jabber:iq:register'
          if e.name == 'instructions'
            instructions = e.text.strip
          else
            fields << e.name
          end
        end
      }
    end

    true
  end

  [instructions, fields]
end
remove_registration() click to toggle source

Remove the registration of a user account

WARNING: this deletes your roster and everything else stored on the server!

# File lib/xmpp4r/client.rb, line 318
def remove_registration
  reg = Iq.new_register
  reg.to = jid.domain
  reg.query.add(REXML::Element.new('remove'))
  send_with_id(reg)
end
start() click to toggle source

Start the stream-parser and send the client-specific stream opening element

Calls superclass method
# File lib/xmpp4r/client.rb, line 86
def start
  super
  send(generate_stream_start(@jid.domain)) { |e|
    if e.name == 'stream'
      true
    else
      false
    end
  }
end
supports_anonymous?() click to toggle source

Reports whether or not anonymous authentication is reported by the client.

Returns true or false

# File lib/xmpp4r/client.rb, line 223
def supports_anonymous?
  @stream_mechanisms.include? 'ANONYMOUS'
end
unbind(desired_resource) click to toggle source

Resource unbinding (RFC3920bis-06 - section 8.6.3.)

# File lib/xmpp4r/client.rb, line 150
def unbind(desired_resource)
  iq = Iq.new(:set)
  unbind = iq.add REXML::Element.new('unbind')
  unbind.add_namespace @stream_features['unbind']
  resource = unbind.add REXML::Element.new('resource')
  resource.text = desired_resource

  send_with_id(iq)
end