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

Attributes

jid  [R]  The client‘s JID

Public Class methods

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

Authenticate with the server

Throws ClientAuthenticationFailure

Authentication mechanisms are used in the following preference:

password:[String]

[Source]

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

See Client#auth_anonymous_sasl

[Source]

     # File lib/xmpp4r/client.rb, line 196
196:     def auth_anonymous
197:       auth_anonymous_sasl
198:     end

Shortcut for anonymous connection to server

Throws ClientAuthenticationFailure

[Source]

     # File lib/xmpp4r/client.rb, line 205
205:     def auth_anonymous_sasl
206:       if self.supports_anonymous?
207:         begin
208:           auth_sasl SASL.new(self, 'ANONYMOUS'), ""
209:         rescue
210:           Jabber::debuglog("#{$!.class}: #{$!}\n#{$!.backtrace.join("\n")}")
211:           raise ClientAuthenticationFailure, $!.to_s
212:         end
213:       else
214:         raise ClientAuthenticationFailure, 'Anonymous authentication unsupported'
215:       end
216:     end

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 234
234:     def auth_nonsasl(password, digest=true)
235:       authset = nil
236:       if digest
237:         authset = Iq.new_authset_digest(@jid, @streamid.to_s, password)
238:       else
239:         authset = Iq.new_authset(@jid, password)
240:       end
241:       send_with_id(authset)
242:       $defout.flush
243: 
244:       true
245:     end

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 170
170:     def auth_sasl(sasl, password)
171:       sasl.auth(password)
172: 
173:       # Restart stream after SASL auth
174:       stop
175:       start
176:       # And wait for features - again
177:       @features_sem.wait
178: 
179:       # Resource binding (RFC3920 - 7)
180:       if @stream_features.has_key? 'bind'
181:         @jid = bind(@jid.resource)
182:       end
183: 
184:       # Session starting
185:       if @stream_features.has_key? 'session'
186:         iq = Iq.new(:set)
187:         session = iq.add REXML::Element.new('session')
188:         session.add_namespace @stream_features['session']
189: 
190:         send_with_id(iq)
191:       end
192:     end

Resource binding (RFC3920bis-06 - section 8.)

XMPP allows to bind to multiple resources

[Source]

     # File lib/xmpp4r/client.rb, line 126
126:     def bind(desired_resource=nil)
127:       iq = Iq.new(:set)
128:       bind = iq.add REXML::Element.new('bind')
129:       bind.add_namespace @stream_features['bind']
130:       if desired_resource
131:         resource = bind.add REXML::Element.new('resource')
132:         resource.text = desired_resource
133:       end
134: 
135:       jid = nil
136:       send_with_id(iq) do |reply|
137:         reply_bind = reply.first_element('bind')
138:         if reply_bind
139:           reported_jid = reply_bind.first_element('jid')
140:           if reported_jid and reported_jid.text
141:             jid = JID.new(reported_jid.text)
142:           end
143:         end
144:       end
145:       jid
146:     end

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

[Source]

    # File lib/xmpp4r/client.rb, line 77
77:     def close
78:       if @status == CONNECTED
79:         send("</stream:stream>")
80:       end
81:       super
82:     end

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

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 334
334:     def password=(new_password)
335:       iq = Iq.new_query(:set, @jid.domain)
336:       iq.query.add_namespace('jabber:iq:register')
337:       iq.query.add(REXML::Element.new('username')).text = @jid.node
338:       iq.query.add(REXML::Element.new('password')).text = new_password
339: 
340:       err = nil
341:       send_with_id(iq)
342:     end

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 303
303:     def register(password, fields={})
304:       reg = Iq.new_register(jid.node, password)
305:       reg.to = jid.domain
306:       fields.each { |name,value|
307:         reg.query.add(REXML::Element.new(name)).text = value
308:       }
309: 
310:       send_with_id(reg)
311:     end

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 250
250:     def register_info
251:       instructions = nil
252:       fields = []
253: 
254:       reg = Iq.new_registerget
255:       reg.to = jid.domain
256:       send_with_id(reg) do |answer|
257:         if answer.query
258:           answer.query.each_element { |e|
259:             if e.namespace == 'jabber:iq:register'
260:               if e.name == 'instructions'
261:                 instructions = e.text.strip
262:               else
263:                 fields << e.name
264:               end
265:             end
266:           }
267:         end
268: 
269:         true
270:       end
271: 
272:       [instructions, fields]
273:     end

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 318
318:     def remove_registration
319:       reg = Iq.new_register
320:       reg.to = jid.domain
321:       reg.query.add(REXML::Element.new('remove'))
322:       send_with_id(reg)
323:     end

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

[Source]

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

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

Returns true or false

[Source]

     # File lib/xmpp4r/client.rb, line 223
223:     def supports_anonymous?
224:       @stream_mechanisms.include? 'ANONYMOUS'
225:     end

Resource unbinding (RFC3920bis-06 - section 8.6.3.)

[Source]

     # File lib/xmpp4r/client.rb, line 150
150:     def unbind(desired_resource)
151:       iq = Iq.new(:set)
152:       unbind = iq.add REXML::Element.new('unbind')
153:       unbind.add_namespace @stream_features['unbind']
154:       resource = unbind.add REXML::Element.new('resource')
155:       resource.text = desired_resource
156: 
157:       send_with_id(iq)
158:     end

[Validate]