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 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
See Client#auth_anonymous_sasl
# File lib/xmpp4r/client.rb, line 169 169: def auth_anonymous 170: auth_anonymous_sasl 171: end
Shortcut for anonymous connection to server
Throws ClientAuthenticationFailure
# 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
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 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
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 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 the connection, sends </stream:stream> tag first
# File lib/xmpp4r/client.rb, line 77 77: def close 78: send("</stream:stream>") 79: super 80: 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 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 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 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
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 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 the registration of a user account
*WARNING:* this deletes your roster and everything else stored on the server!
# 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 the stream-parser and send the client-specific stream opening element
# 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