Class: Jabber::Client

Inherits:
Connection show all
Defined in:
lib/gems/xmpp4r-0.4/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

HTTPBinding::Client

Constant Summary

Constants inherited from Stream

Stream::CONNECTED, Stream::DISCONNECTED

Instance Attribute Summary collapse

Attributes inherited from Connection

#allow_tls, #features_timeout, #host, #keepalive_interval, #port, #ssl_capath, #ssl_verifycb, #use_ssl

Attributes inherited from Stream

#fd, #status

Instance Method Summary collapse

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, #is_connected?, #is_disconnected?, #on_exception, #parse_failure, #parser_end, #receive, #send, #send_data, #send_with_id, #stop

Constructor Details

#initialize(jid) ⇒ Client

Create a new Client.

Remember to always put a resource in your JID unless the server can do SASL.



27
28
29
30
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/client.rb', line 27

def initialize(jid)
  super()
  @jid = (jid.kind_of?(JID) ? jid : JID.new(jid.to_s))
end

Instance Attribute Details

#jidObject (readonly)

The client’s JID



21
22
23
# File 'lib/gems/xmpp4r-0.4/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


105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/client.rb', line 105

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_anonymousObject

See Client#auth_anonymous_sasl



169
170
171
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/client.rb', line 169

def auth_anonymous
  auth_anonymous_sasl
end

#auth_anonymous_saslObject

Shortcut for anonymous connection to server

Throws ClientAuthenticationFailure



178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/client.rb', line 178

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



207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/client.rb', line 207

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


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/client.rb', line 130

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'
    iq = Iq.new(:set)
    bind = iq.add REXML::Element.new('bind')
    bind.add_namespace @stream_features['bind']
    if jid.resource
      resource = bind.add REXML::Element.new('resource')
      resource.text = jid.resource
    end

    send_with_id(iq) do |reply|
      reported_jid = reply.first_element('jid')
      if reported_jid and reported_jid.text
        @jid = JID.new(reported_jid.text)
      end
    end
  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

#closeObject

Close the connection, sends </stream:stream> tag first



77
78
79
80
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/client.rb', line 77

def close
  send("</stream:stream>")
  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/gems/xmpp4r-0.4/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



307
308
309
310
311
312
313
314
315
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/client.rb', line 307

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)’



276
277
278
279
280
281
282
283
284
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/client.rb', line 276

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_infoObject

Get instructions and available fields for registration

return
instructions, fields

Where instructions is a String and fields is an Array of Strings



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/client.rb', line 223

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_registrationObject

Remove the registration of a user account

WARNING: this deletes your roster and everything else stored on the server!



291
292
293
294
295
296
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/client.rb', line 291

def remove_registration
  reg = Iq.new_register
  reg.to = jid.domain
  reg.query.add(REXML::Element.new('remove'))
  send_with_id(reg)
end

#startObject

Start the stream-parser and send the client-specific stream opening element



84
85
86
87
88
89
90
91
92
93
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/client.rb', line 84

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

Returns:

  • (Boolean)


196
197
198
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/client.rb', line 196

def supports_anonymous?
  @stream_mechanisms.include? 'ANONYMOUS'
end