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
21 22 23 |
# File 'lib/xmpp4r/client.rb', line 21 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
107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# 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 |
#auth_anonymous ⇒ Object
See Client#auth_anonymous_sasl
196 197 198 |
# File 'lib/xmpp4r/client.rb', line 196 def auth_anonymous auth_anonymous_sasl end |
#auth_anonymous_sasl ⇒ Object
Shortcut for anonymous connection to server
Throws ClientAuthenticationFailure
205 206 207 208 209 210 211 212 213 214 215 216 |
# 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 |
#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
234 235 236 237 238 239 240 241 242 243 244 245 |
# 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 |
#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
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# 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 |
#bind(desired_resource = nil) ⇒ Object
Resource binding (RFC3920bis-06 - section 8.)
XMPP allows to bind to multiple resources
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# 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 ⇒ Object
Close the connection, sends </stream:stream>
tag first
77 78 79 80 81 82 |
# File 'lib/xmpp4r/client.rb', line 77 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
42 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 |
# 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 |
#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
334 335 336 337 338 339 340 341 342 |
# 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(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)’
303 304 305 306 307 308 309 310 311 |
# 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 |
#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
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# 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_registration ⇒ Object
Remove the registration of a user account
WARNING: this deletes your roster and everything else stored on the server!
318 319 320 321 322 323 |
# 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 ⇒ Object
Start the stream-parser and send the client-specific stream opening element
86 87 88 89 90 91 92 93 94 95 |
# 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 |
#supports_anonymous? ⇒ Boolean
Reports whether or not anonymous authentication is reported by the client.
Returns true or false
223 224 225 |
# File 'lib/xmpp4r/client.rb', line 223 def supports_anonymous? @stream_mechanisms.include? 'ANONYMOUS' end |
#unbind(desired_resource) ⇒ Object
Resource unbinding (RFC3920bis-06 - section 8.6.3.)
150 151 152 153 154 155 156 157 158 |
# 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 |