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.
Authenticate with the server
Throws ClientAuthenticationFailure
Authentication mechanisms are used in the following preference:
password |
|
# 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
See Client#auth_anonymous_sasl
# File lib/xmpp4r/client.rb, line 196 def auth_anonymous auth_anonymous_sasl end
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
Send auth with given password and wait for result (non-SASL)
Throws ServerError
password |
|
digest |
|
# 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
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 |
|
# 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
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 the connection, sends </stream:stream> tag first
# File lib/xmpp4r/client.rb, line 77 def close if @status == CONNECTED send("</stream:stream>") end super 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 |
|
use_ssl |
|
return |
self |
# 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
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 |
|
# 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 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 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
Get instructions and available fields for registration
return |
|
# 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 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 the stream-parser and send the client-specific stream opening element
# 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
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
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
Generated with the Darkfish Rdoc Generator 2.