Class: Jabber::Roster::Helper

Inherits:
Object
  • Object
show all
Defined in:
lib/gems/xmpp4r-0.4/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) ⇒ 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
71
72
73
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/roster/helper/roster.rb', line 39

def initialize(stream)
  @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
  }

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

Instance Attribute Details

#itemsObject (readonly)

All items in your roster

items
Hash

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



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



244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/roster/helper/roster.rb', line 244

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



347
348
349
350
351
352
353
354
355
356
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/roster/helper/roster.rb', line 347

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



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/roster/helper/roster.rb', line 323

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)



116
117
118
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/roster/helper/roster.rb', line 116

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



89
90
91
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/roster/helper/roster.rb', line 89

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



131
132
133
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/roster/helper/roster.rb', line 131

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


154
155
156
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/roster/helper/roster.rb', line 154

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



102
103
104
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/roster/helper/roster.rb', line 102

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



361
362
363
364
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/roster/helper/roster.rb', line 361

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.



261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/roster/helper/roster.rb', line 261

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]



297
298
299
300
301
302
303
304
305
306
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/roster/helper/roster.rb', line 297

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

#groupsObject

Groups in this Roster, sorted by name

Contains nil if there are ungrouped items

result
Array

containing group names (String)



280
281
282
283
284
285
286
287
288
289
# File 'lib/gems/xmpp4r-0.4/lib/xmpp4r/roster/helper/roster.rb', line 280

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



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

def wait_for_roster
  @roster_wait.wait
  @roster_wait.run
end