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. If threaded mode is activated, callbacks are called as soon as messages are received; If it isn‘t, you have to call Stream#process from time to time.

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

[Source]

    # File lib/xmpp4r/client.rb, line 31
31:     def initialize(jid, threaded = true)
32:       super(threaded)
33:       @jid = (jid.kind_of?(JID) ? jid : JID.new(jid.to_s))
34:     end

Public Instance methods

Authenticate with the server

Throws AuthenticationFailure

Authentication mechanisms are used in the following preference:

password:[String]

[Source]

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

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

Throws ErrorException

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

[Source]

     # File lib/xmpp4r/client.rb, line 180
180:     def auth_nonsasl(password, digest=true)
181:       authset = nil
182:       if digest
183:         authset = Iq::new_authset_digest(@jid, @streamid.to_s, password)
184:       else
185:         authset = Iq::new_authset(@jid, password)
186:       end
187:       send_with_id(authset) do |r|
188:         true
189:       end
190:       $defout.flush
191: 
192:       true
193:     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 133
133:     def auth_sasl(sasl, password)
134:       sasl.auth(password)
135: 
136:       # Restart stream after SASL auth
137:       stop
138:       start
139:       # And wait for features - again
140:       @features_lock.lock
141:       @features_lock.unlock
142: 
143:       # Resource binding (RFC3920 - 7)
144:       if @stream_features.has_key? 'bind'
145:         iq = Iq.new(:set)
146:         bind = iq.add REXML::Element.new('bind')
147:         bind.add_namespace @stream_features['bind']
148:         if jid.resource
149:           resource = bind.add REXML::Element.new('resource')
150:           resource.text = jid.resource
151:         end
152: 
153:         send_with_id(iq) { |reply|
154:           reported_jid = reply.first_element('jid')
155:           if reply.type == :result and reported_jid and reported_jid.text
156:             @jid = JID.new(reported_jid.text)
157:           end
158: 
159:           true
160:         }
161:       end
162: 
163:       # Session starting
164:       if @stream_features.has_key? 'session'
165:         iq = Iq.new(:set)
166:         session = iq.add REXML::Element.new('session')
167:         session.add_namespace @stream_features['session']
168: 
169:         send_with_id(iq) { true }
170:       end
171:     end

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

[Source]

    # File lib/xmpp4r/client.rb, line 80
80:     def close
81:       send("</stream:stream>")
82:       super
83:     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
return:self

[Source]

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

Change the client‘s password

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

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

new_password:[String] New password

[Source]

     # File lib/xmpp4r/client.rb, line 233
233:     def password=(new_password)
234:       iq = Iq::new_query(:set, @jid.domain)
235:       iq.query.add_namespace('jabber:iq:register')
236:       iq.query.add(REXML::Element.new('username')).text = @jid.node
237:       iq.query.add(REXML::Element.new('password')).text = new_password
238: 
239:       err = nil
240:       send_with_id(iq) { |answer|
241:         if answer.type == :result
242:           true
243:         else
244:           false
245:         end
246:       }
247:     end

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

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

[Source]

     # File lib/xmpp4r/client.rb, line 201
201:     def register(password)
202:       reg = Iq.new_register(jid.node, password)
203:       reg.to = jid.domain
204:       send_with_id(reg) { |answer|
205:         true
206:       }
207:     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 214
214:     def remove_registration
215:       reg = Iq.new_register
216:       reg.to = jid.domain
217:       reg.query.add(REXML::Element.new('remove'))
218:       send_with_id(reg) { |answer|
219:         p answer.to_s
220:         true
221:       }
222:     end

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

[Source]

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

[Validate]