Class: Jabber::Roster::Helper

Inherits:
Object
  • Object
show all
Defined in:
lib/xmpp4r/roster/helper/roster.rb

Overview

The Roster helper intercepts <iq/> stanzas with Jabber::IqQueryRoster and <presence/> stanzas, but provides cbs which allow the programmer to keep track of updates.

A thread for any received stanza is spawned, so the user can invoke accept_subscription et al in the callback blocks, without stopping the current (= parser) thread when waiting for a reply.

Defined Under Namespace

Classes: RosterItem

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stream, startnow = true) ⇒ Helper

Initialize a new Roster helper

Registers its cbs (prio = 120, ref = self)

Request a roster (Remember to send initial presence afterwards!)

The initialization will not wait for the roster being received, use wait_for_roster.

Attention: If you send presence and receive presences before the roster has arrived, the Roster helper will let them pass through and does not keep them!



39
40
41
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
# File 'lib/xmpp4r/roster/helper/roster.rb', line 39

def initialize(stream, startnow = true)
  @stream = stream
  @items = {}
  @items_lock = Mutex.new
  @roster_wait = Semaphore.new
  @query_cbs = CallbackList.new
  @update_cbs = CallbackList.new
  @presence_cbs = CallbackList.new
  @subscription_cbs = CallbackList.new
  @subscription_request_cbs = CallbackList.new

  # Register cbs
  stream.add_iq_callback(120, self) { |iq|
    if iq.query.kind_of?(IqQueryRoster)
      Thread.new do
        Thread.current.abort_on_exception = true
        handle_iq_query_roster(iq)
      end

      true
    else
      false
    end
  }
  stream.add_presence_callback(120, self) { |pres|
    Thread.new do
      Thread.current.abort_on_exception = true
      handle_presence(pres)
    end
  }
  get_roster if startnow
end

Instance Attribute Details

#itemsObject (readonly)

All items in your roster

items
Hash

([JID] => [Roster::Helper::RosterItem])



23
24
25
# File 'lib/xmpp4r/roster/helper/roster.rb', line 23

def items
  @items
end

Instance Method Details

#[](jid) ⇒ Object

Get an item by jid

If not available tries to look for it with the resource stripped



247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/xmpp4r/roster/helper/roster.rb', line 247

def [](jid)
  jid = JID.new(jid) unless jid.kind_of? JID

  @items_lock.synchronize {
    if @items.has_key?(jid)
      @items[jid]
    elsif @items.has_key?(jid.strip)
      @items[jid.strip]
    else
      nil
    end
  }
end

#accept_subscription(jid, iname = nil) ⇒ Object

Accept a subscription request

  • Sends a <presence type='subscribed'/> stanza

  • Adds the contact to your roster

jid
JID

of contact

iname
String

Optional roster item name



350
351
352
353
354
355
356
357
358
359
# File 'lib/xmpp4r/roster/helper/roster.rb', line 350

def accept_subscription(jid, iname=nil)
  pres = Presence.new.set_type(:subscribed).set_to(jid.strip)
  @stream.send(pres)

  unless self[jid.strip]
    request = Iq.new_rosterset
    request.query.add(Jabber::Roster::RosterItem.new(jid.strip, iname))
    @stream.send_with_id(request)
  end
end

#add(jid, iname = nil, subscribe = false) ⇒ Object

Add a user to your roster

Threading is encouraged as the function waits for a result. ServerError is thrown upon error.

See Jabber::Roster::Helper::RosterItem#subscribe for details about subscribing. (This method isn't used here but the same functionality applies.)

If the item is already in the local roster it will simply send itself

jid
JID

to add

iname
String

Optional item name

subscribe
Boolean

Whether to subscribe to this jid



326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/xmpp4r/roster/helper/roster.rb', line 326

def add(jid, iname=nil, subscribe=false)
  if self[jid]
    self[jid].send
  else
    request = Iq.new_rosterset
    request.query.add(Jabber::Roster::RosterItem.new(jid, iname))
    @stream.send_with_id(request)
    # Adding to list is handled by handle_iq_query_roster
  end

  if subscribe
    # Actually the item *should* already be known now,
    # but we do it manually to exclude conditions.
    pres = Presence.new.set_type(:subscribe).set_to(jid.strip)
    @stream.send(pres)
  end
end

#add_presence_callback(prio = 0, ref = nil, &block) ⇒ Object

Add a callback for Jabber::Presence updates

This will be called for <presence/> stanzas for known RosterItems. Unknown JIDs may still pass and can be caught via Jabber::Stream#add_presence_callback.

The block receives three objects:

  • the Jabber::Roster::Helper::RosterItem

  • the old Jabber::Presence (or nil)

  • the new Jabber::Presence (or nil)



119
120
121
# File 'lib/xmpp4r/roster/helper/roster.rb', line 119

def add_presence_callback(prio = 0, ref = nil, &block)
  @presence_cbs.add(prio, ref, block)
end

#add_query_callback(prio = 0, ref = nil, &block) ⇒ Object

Add a callback to be called when a query has been processed

Because update callbacks are called for each roster item, this may be appropriate to notify that anything has updated.

Arguments for callback block: The received <iq/> stanza



92
93
94
# File 'lib/xmpp4r/roster/helper/roster.rb', line 92

def add_query_callback(prio = 0, ref = nil, &block)
  @query_cbs.add(prio, ref, block)
end

#add_subscription_callback(prio = 0, ref = nil, &block) ⇒ Object

Add a callback for subscription updates, which will be called upon receiving a <presence/> stanza with type:

  • :subscribed

  • :unsubscribe

  • :unsubscribed

The block receives two objects:

  • the Jabber::Roster::Helper::RosterItem (or nil)

  • the <presence/> stanza



134
135
136
# File 'lib/xmpp4r/roster/helper/roster.rb', line 134

def add_subscription_callback(prio = 0, ref = nil, &block)
  @subscription_cbs.add(prio, ref, block)
end

#add_subscription_request_callback(prio = 0, ref = nil, &block) ⇒ Object

Add a callback for subscription requests, which will be called upon receiving a <presence type='subscribe'/> stanza

The block receives two objects:

  • the Jabber::Roster::Helper::RosterItem (or nil)

  • the <presence/> stanza

Response to this event can be taken with accept_subscription and decline_subscription.

Example usage:

my_roster.add_subscription_request_callback do |item,presence|
  if accept_subscription_requests
    my_roster.accept_subscription(presence.from)
  else
    my_roster.decline_subscription(presence.from)
  end
end


157
158
159
# File 'lib/xmpp4r/roster/helper/roster.rb', line 157

def add_subscription_request_callback(prio = 0, ref = nil, &block)
  @subscription_request_cbs.add(prio, ref, block)
end

#add_update_callback(prio = 0, ref = nil, &block) ⇒ Object

Add a callback for Jabber::Roster::Helper::RosterItem updates

Note that this will be called much after initialization for the answer of the initial roster request

The block receives two objects:

  • the old Jabber::Roster::Helper::RosterItem

  • the new Jabber::Roster::Helper::RosterItem



105
106
107
# File 'lib/xmpp4r/roster/helper/roster.rb', line 105

def add_update_callback(prio = 0, ref = nil, &block)
  @update_cbs.add(prio, ref, block)
end

#decline_subscription(jid) ⇒ Object

Decline a subscription request

  • Sends a <presence type='unsubscribed'/> stanza



364
365
366
367
# File 'lib/xmpp4r/roster/helper/roster.rb', line 364

def decline_subscription(jid)
  pres = Presence.new.set_type(:unsubscribed).set_to(jid.strip)
  @stream.send(pres)
end

#find(jid) ⇒ Object

Returns the list of RosterItems which, stripped, are equal to the one you are looking for.



264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/xmpp4r/roster/helper/roster.rb', line 264

def find(jid)
  jid = JID.new(jid) unless jid.kind_of? JID

  j = jid.strip
  l = {}
  @items_lock.synchronize {
    @items.each_pair do |k, v|
      l[k] = v if k.strip == j
    end
  }
  l
end

#find_by_group(group) ⇒ Object

Get items in a group

When group is nil, return ungrouped items

group
String

Group name

result

Array of [RosterItem]



300
301
302
303
304
305
306
307
308
309
# File 'lib/xmpp4r/roster/helper/roster.rb', line 300

def find_by_group(group)
  res = []
  @items_lock.synchronize {
    @items.each_pair do |jid,item|
      res.push(item) if item.groups.include?(group)
      res.push(item) if item.groups == [] and group.nil?
    end
  }
  res
end

#get_rosterObject



72
73
74
75
76
# File 'lib/xmpp4r/roster/helper/roster.rb', line 72

def get_roster
  # Request the roster
  rosterget = Iq.new_rosterget
  @stream.send(rosterget)
end

#groupsObject

Groups in this Roster, sorted by name

Contains nil if there are ungrouped items

result
Array

containing group names (String)



283
284
285
286
287
288
289
290
291
292
# File 'lib/xmpp4r/roster/helper/roster.rb', line 283

def groups
  res = []
  @items_lock.synchronize {
    @items.each_pair do |jid,item|
      res += item.groups
      res += [nil] if item.groups == []
    end
  }
  res.uniq.sort { |a,b| a.to_s <=> b.to_s }
end

#wait_for_rosterObject

Wait for first roster query result to arrive



80
81
82
83
# File 'lib/xmpp4r/roster/helper/roster.rb', line 80

def wait_for_roster
  @roster_wait.wait
  @roster_wait.run
end