Class: Jabber::Client
- Inherits:
-
Connection
- Object
- Stream
- Connection
- Jabber::Client
- Defined in:
- lib/xmpp4r/client.rb
Overview
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.
Direct Known Subclasses
Constant Summary
Constants inherited from Stream
Stream::CONNECTED, Stream::DISCONNECTED
Instance Attribute Summary collapse
-
#jid ⇒ Object
readonly
The client’s JID.
Attributes inherited from Connection
#allow_tls, #features_timeout, #host, #keepalive_interval, #port, #ssl_capath, #ssl_verifycb, #use_ssl
Attributes inherited from Stream
Instance Method Summary collapse
-
#auth(password) ⇒ Object
Authenticate with the server.
-
#auth_anonymous ⇒ Object
See Client#auth_anonymous_sasl.
-
#auth_anonymous_sasl ⇒ Object
Shortcut for anonymous connection to server.
-
#auth_nonsasl(password, digest = true) ⇒ Object
Send auth with given password and wait for result (non-SASL).
-
#auth_sasl(sasl, password) ⇒ Object
Use a SASL authentication mechanism and bind to a resource.
-
#bind(desired_resource = nil) ⇒ Object
Resource binding (RFC3920bis-06 - section 8.).
-
#close ⇒ Object
Close the connection, sends
</stream:stream>
tag first. -
#connect(host = nil, port = 5222) ⇒ Object
connect to the server (chaining-friendly).
-
#initialize(jid) ⇒ Client
constructor
Create a new Client.
-
#password=(new_password) ⇒ Object
Change the client’s password.
-
#register(password, fields = {}) ⇒ Object
Register a new user account (may be used instead of Client#auth).
-
#register_info ⇒ Object
- Get instructions and available fields for registration return
- instructions, fields
-
Where instructions is a String and fields is an Array of Strings.
-
#remove_registration ⇒ Object
Remove the registration of a user account.
-
#start ⇒ Object
Start the stream-parser and send the client-specific stream opening element.
-
#supports_anonymous? ⇒ Boolean
Reports whether or not anonymous authentication is reported by the client.
-
#unbind(desired_resource) ⇒ Object
Resource unbinding (RFC3920bis-06 - section 8.6.3.).
Methods inherited from Connection
#accept_features, #close!, #is_tls?, #starttls
Methods inherited from Stream
#add_iq_callback, #add_message_callback, #add_presence_callback, #add_stanza_callback, #add_xml_callback, #close!, #delete_iq_callback, #delete_message_callback, #delete_presence_callback, #delete_stanza_callback, #delete_xml_callback, #iq_callbacks, #is_connected?, #is_disconnected?, #message_callbacks, #on_exception, #parse_failure, #parser_end, #presence_callbacks, #receive, #send, #send_data, #send_with_id, #stanza_callbacks, #stop, #xml_callbacks
Constructor Details
Instance Attribute Details
#jid ⇒ Object (readonly)
The client’s JID
22 23 24 |
# File 'lib/xmpp4r/client.rb', line 22 def jid @jid end |
Instance Method Details
#auth(password) ⇒ Object
Authenticate with the server
Throws ClientAuthenticationFailure
Authentication mechanisms are used in the following preference:
-
SASL DIGEST-MD5
-
SASL PLAIN
-
Non-SASL digest
- password
- String
108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/xmpp4r/client.rb', line 108 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 |
#auth_anonymous ⇒ Object
See Client#auth_anonymous_sasl
197 198 199 |
# File 'lib/xmpp4r/client.rb', line 197 def auth_anonymous auth_anonymous_sasl end |
#auth_anonymous_sasl ⇒ Object
Shortcut for anonymous connection to server
Throws ClientAuthenticationFailure
206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/xmpp4r/client.rb', line 206 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 |
#auth_nonsasl(password, digest = true) ⇒ Object
Send auth with given password and wait for result (non-SASL)
Throws ServerError
- password
- String
-
the password
- digest
- Boolean
-
use Digest authentication
235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/xmpp4r/client.rb', line 235 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 |
#auth_sasl(sasl, password) ⇒ Object
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
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/xmpp4r/client.rb', line 171 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 |
#bind(desired_resource = nil) ⇒ Object
Resource binding (RFC3920bis-06 - section 8.)
XMPP allows to bind to multiple resources
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/xmpp4r/client.rb', line 127 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 ⇒ Object
Close the connection, sends </stream:stream>
tag first
78 79 80 81 82 83 |
# File 'lib/xmpp4r/client.rb', line 78 def close if @status == CONNECTED send("</stream:stream>") end super end |
#connect(host = nil, port = 5222) ⇒ Object
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
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/xmpp4r/client.rb', line 43 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 |
#password=(new_password) ⇒ Object
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
335 336 337 338 339 340 341 342 343 |
# File 'lib/xmpp4r/client.rb', line 335 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(password, fields = {}) ⇒ Object
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)’
304 305 306 307 308 309 310 311 312 |
# File 'lib/xmpp4r/client.rb', line 304 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 |
#register_info ⇒ Object
Get instructions and available fields for registration
- return
- instructions, fields
-
Where instructions is a String and fields is an Array of Strings
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/xmpp4r/client.rb', line 251 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_registration ⇒ Object
Remove the registration of a user account
WARNING: this deletes your roster and everything else stored on the server!
319 320 321 322 323 324 |
# File 'lib/xmpp4r/client.rb', line 319 def remove_registration reg = Iq.new_register reg.to = jid.domain reg.query.add(REXML::Element.new('remove')) send_with_id(reg) end |
#start ⇒ Object
Start the stream-parser and send the client-specific stream opening element
87 88 89 90 91 92 93 94 95 96 |
# File 'lib/xmpp4r/client.rb', line 87 def start super send(generate_stream_start(@jid.domain)) { |e| if e.name == 'stream' true else false end } end |
#supports_anonymous? ⇒ Boolean
Reports whether or not anonymous authentication is reported by the client.
Returns true or false
224 225 226 |
# File 'lib/xmpp4r/client.rb', line 224 def supports_anonymous? @stream_mechanisms.include? 'ANONYMOUS' end |
#unbind(desired_resource) ⇒ Object
Resource unbinding (RFC3920bis-06 - section 8.6.3.)
151 152 153 154 155 156 157 158 159 |
# File 'lib/xmpp4r/client.rb', line 151 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 |