| 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.
| jid | [R] | The client‘s JID |
Authenticate with the server
Throws ClientAuthenticationFailure
Authentication mechanisms are used in the following preference:
| password: | [String] |
# 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
# 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
# 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 |
# 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] |
# 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
# 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
# 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 |
# 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 |
# 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)’
# 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 |
# 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!
# 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
# 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
# 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.)
# 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