Class: Jabber::Client

Class | Jabber::Client
In: | lib/xmpp4r/client.rb
Parent: | Connection

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 Stream#on_exception and re-call Client#connect and Client#auth. Don‘t forget to re-send initial Presence and everything else you need to setup your session.

### Methods

auth auth_anonymous auth_anonymous_sasl auth_nonsasl auth_sasl close connect new password= register register_info remove_registration start supports_anonymous?

### Attributes

jid | [R] | The client‘s JID

### Public Class methods

new(jid)

Create a new Client.

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

[Source]

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

### Public Instance methods

auth(password)

Authenticate with the server

Throws ClientAuthenticationFailure

Authentication mechanisms are used in the following preference:

- SASL DIGEST-MD5
- SASL PLAIN
- Non-SASL digest

password: | [String]

[Source]

         # File lib/xmpp4r/client.rb, line 105
    105:     def auth(password)
    106:       begin
    107:         if @stream_mechanisms.include? 'DIGEST-MD5'
    108:           auth_sasl SASL.new(self, 'DIGEST-MD5'), password
    109:         elsif @stream_mechanisms.include? 'PLAIN'
    110:           auth_sasl SASL.new(self, 'PLAIN'), password
    111:         else
    112:           auth_nonsasl(password)
    113:         end
    114:       rescue
    115:         Jabber::debuglog("#{$!.class}: #{$!}\n#{$!.backtrace.join("\n")}")
    116:         raise ClientAuthenticationFailure.new, $!.to_s
    117:       end
    118:     end

auth_anonymous()

See Client#auth_anonymous_sasl

[Source]

         # File lib/xmpp4r/client.rb, line 169
    169:     def auth_anonymous
    170:       auth_anonymous_sasl
    171:     end

auth_anonymous_sasl()

Shortcut for anonymous connection to server

Throws ClientAuthenticationFailure

[Source]

         # File lib/xmpp4r/client.rb, line 178
    178:     def auth_anonymous_sasl
    179:       if self.supports_anonymous?
    180:         begin
    181:           auth_sasl SASL.new(self, 'ANONYMOUS'), ""
    182:         rescue
    183:           Jabber::debuglog("#{$!.class}: #{$!}\n#{$!.backtrace.join("\n")}")
    184:           raise ClientAuthenticationFailure, $!.to_s
    185:         end
    186:       else
    187:         raise ClientAuthenticationFailure, 'Anonymous authentication unsupported'
    188:       end
    189:     end

auth_nonsasl(password, digest=true)

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

Throws ServerError

password: | [String] the password
digest: | [Boolean] use Digest authentication

[Source]

         # File lib/xmpp4r/client.rb, line 207
    207:     def auth_nonsasl(password, digest=true)
    208:       authset = nil
    209:       if digest
    210:         authset = Iq.new_authset_digest(@jid, @streamid.to_s, password)
    211:       else
    212:         authset = Iq.new_authset(@jid, password)
    213:       end
    214:       send_with_id(authset)
    215:       $defout.flush
    216:
    217:       true
    218:     end

auth_sasl(sasl, password)

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, Client#auth may look for the best mechanism suitable.

sasl: | Descendant of [Jabber::SASL::Base]
password: | [String]

[Source]

         # File lib/xmpp4r/client.rb, line 130
    130:     def auth_sasl(sasl, password)
    131:       sasl.auth(password)
    132:
    133:       # Restart stream after SASL auth
    134:       stop
    135:       start
    136:       # And wait for features - again
    137:       @features_sem.wait
    138:
    139:       # Resource binding (RFC3920 - 7)
    140:       if @stream_features.has_key? 'bind'
    141:         iq = Iq.new(:set)
    142:         bind = iq.add REXML::Element.new('bind')
    143:         bind.add_namespace @stream_features['bind']
    144:         if jid.resource
    145:           resource = bind.add REXML::Element.new('resource')
    146:           resource.text = jid.resource
    147:         end
    148:
    149:         send_with_id(iq) do |reply|
    150:           reported_jid = reply.first_element('jid')
    151:           if reported_jid and reported_jid.text
    152:             @jid = JID.new(reported_jid.text)
    153:           end
    154:         end
    155:       end
    156:
    157:       # Session starting
    158:       if @stream_features.has_key? 'session'
    159:         iq = Iq.new(:set)
    160:         session = iq.add REXML::Element.new('session')
    161:         session.add_namespace @stream_features['session']
    162:
    163:         send_with_id(iq)
    164:       end
    165:     end

close()

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

[Source]

        # File lib/xmpp4r/client.rb, line 77
    77:     def close
    78:       send("</stream:stream>")
    79:       super
    80:     end

connect(host = nil, port = 5222)

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

[Source]

        # File lib/xmpp4r/client.rb, line 42
    42:     def connect(host = nil, port = 5222)
    43:       if host.nil?
    44:         begin
    45:           srv = []
    46:           Resolv::DNS.open { |dns|
    47:             # If ruby version is too old and SRV is unknown, this will raise a NameError
    48:             # which is caught below
    49:             Jabber::debuglog("RESOLVING:\n_xmpp-client._tcp.#{@jid.domain} (SRV)")
    50:             srv = dns.getresources("_xmpp-client._tcp.#{@jid.domain}", Resolv::DNS::Resource::IN::SRV)
    51:           }
    52:           # Sort SRV records: lowest priority first, highest weight first
    53:           srv.sort! { |a,b| (a.priority != b.priority) ? (a.priority <=> b.priority) : (b.weight <=> a.weight) }
    54:
    55:           srv.each { |record|
    56:             begin
    57:               connect(record.target.to_s, record.port)
    58:               # Success
    59:               return self
    60:             rescue SocketError, Errno::ECONNREFUSED
    61:               # Try next SRV record
    62:             end
    63:           }
    64:         rescue NameError
    65:           Jabber::debuglog "Resolv::DNS does not support SRV records. Please upgrade to ruby-1.8.3 or later!"
    66:         end
    67:         # Fallback to normal connect method
    68:       end
    69:
    70:       super(host.nil? ? jid.domain : host, port)
    71:       self
    72:     end

password=(new_password)

Change the client‘s password

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

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

new_password: | [String] New password

[Source]

         # File lib/xmpp4r/client.rb, line 307
    307:     def password=(new_password)
    308:       iq = Iq.new_query(:set, @jid.domain)
    309:       iq.query.add_namespace('jabber:iq:register')
    310:       iq.query.add(REXML::Element.new('username')).text = @jid.node
    311:       iq.query.add(REXML::Element.new('password')).text = new_password
    312:
    313:       err = nil
    314:       send_with_id(iq)
    315:     end

register(password, fields={})

Register a new user account (may be used instead of Client#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)’

[Source]

         # File lib/xmpp4r/client.rb, line 276
    276:     def register(password, fields={})
    277:       reg = Iq.new_register(jid.node, password)
    278:       reg.to = jid.domain
    279:       fields.each { |name,value|
    280:         reg.query.add(REXML::Element.new(name)).text = value
    281:       }
    282:
    283:       send_with_id(reg)
    284:     end

register_info()

Get instructions and available fields for registration

return: | [instructions, fields] Where instructions is a String and fields is an Array of Strings

[Source]

         # File lib/xmpp4r/client.rb, line 223
    223:     def register_info
    224:       instructions = nil
    225:       fields = []
    226:
    227:       reg = Iq.new_registerget
    228:       reg.to = jid.domain
    229:       send_with_id(reg) do |answer|
    230:         if answer.query
    231:           answer.query.each_element { |e|
    232:             if e.namespace == 'jabber:iq:register'
    233:               if e.name == 'instructions'
    234:                 instructions = e.text.strip
    235:               else
    236:                 fields << e.name
    237:               end
    238:             end
    239:           }
    240:         end
    241:
    242:         true
    243:       end
    244:
    245:       [instructions, fields]
    246:     end

remove_registration()

Remove the registration of a user account

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

[Source]

         # File lib/xmpp4r/client.rb, line 291
    291:     def remove_registration
    292:       reg = Iq.new_register
    293:       reg.to = jid.domain
    294:       reg.query.add(REXML::Element.new('remove'))
    295:       send_with_id(reg)
    296:     end

start()

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

[Source]

        # File lib/xmpp4r/client.rb, line 84
    84:     def start
    85:       super
    86:       send(generate_stream_start(@jid.domain)) { |e|
    87:         if e.name == 'stream'
    88:           true
    89:         else
    90:           false
    91:         end
    92:       }
    93:     end

supports_anonymous?()

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

Returns true or false

[Source]

         # File lib/xmpp4r/client.rb, line 196
    196:     def supports_anonymous?
    197:       @stream_mechanisms.include? 'ANONYMOUS'
    198:     end

[Validate]