Class Jabber::Roster::Helper
In: lib/xmpp4r/roster/helper/roster.rb
Parent: Object
XMPPElement ErrorResponse XMPPStanza X IqQuery Message Presence Iq JabberError ComponentAuthenticationFailure ArgumentError SOCKS5Error ServerError NoNameXmlnsRegistered ClientAuthenticationFailure Connection Client Component Client Singleton IdGenerator Comparable JID StandardError REXML::Element Stream IqQuery IqQueryRoster IqQueryBytestreams IqQueryVersion IqQueryRPC IqQueryMUCOwner IqQueryMUCAdmin IqQueryDiscoItems IqQueryDiscoInfo XRosterItem RosterXItem XMPPElement RosterItem StreamHost IqSiFile IqSiFileRange IqSi StreamHostUsed IqFeature C Body HTML UserItem XMUCUserInvite Configuration Items Item IqPubSub Publish Event IqPubSubOwner Subscription Unsubscribe Tune XDataField XDataReported XDataTitle XDataInstructions Feature Item Identity IqVcard XRoster RosterX X XMUC XMUCUser XDelay XData Iq IqCommand SOCKS5Bytestreams SOCKS5BytestreamsTarget SOCKS5BytestreamsInitiator SOCKS5BytestreamsServerStreamHost TCPSocket SOCKS5Socket IBB IBBTarget IBBInitiator Responder SimpleResponder XMLRPC::ParserWriterChooseMixin Client Server XMLRPC::ParseContentType XMLRPC::BasicServer MUCClient SimpleMUCClient MUC::UserItem XMUCUserItem IqQueryMUCAdminItem XParent SubscriptionConfig NodeConfig OwnerNodeConfig EventItems EventItem ServiceHelper NodeHelper Base Anonymous DigestMD5 Plain PubSub::ServiceHelper Helper FileSource Base Bot CallbackList Callback StreamParser Semaphore Helper Responder SOCKS5BytestreamsPeer SOCKS5BytestreamsServer IBBQueueItem Helper MUCBrowser NodeBrowser Helper Responder Helper lib/xmpp4r/message.rb lib/xmpp4r/connection.rb lib/xmpp4r/xmppstanza.rb lib/xmpp4r/iq.rb lib/xmpp4r/callbacks.rb lib/xmpp4r/idgenerator.rb lib/xmpp4r/stream.rb lib/xmpp4r/client.rb lib/xmpp4r/jid.rb lib/xmpp4r/x.rb lib/xmpp4r/streamparser.rb lib/xmpp4r/semaphore.rb lib/xmpp4r/errors.rb lib/xmpp4r/component.rb lib/xmpp4r/presence.rb lib/xmpp4r/xmppelement.rb lib/xmpp4r/query.rb lib/xmpp4r/roster/x/roster.rb lib/xmpp4r/roster/helper/roster.rb lib/xmpp4r/roster/iq/roster.rb Roster lib/xmpp4r/command/iq/command.rb lib/xmpp4r/command/helper/responder.rb Command XParent lib/xmpp4r/bytestreams/iq/si.rb lib/xmpp4r/bytestreams/helper/ibb/initiator.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb lib/xmpp4r/bytestreams/iq/bytestreams.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb lib/xmpp4r/bytestreams/helper/ibb/target.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb lib/xmpp4r/bytestreams/helper/ibb/base.rb Bytestreams lib/xmpp4r/version/helper/responder.rb lib/xmpp4r/version/helper/simpleresponder.rb lib/xmpp4r/version/iq/version.rb Version lib/xmpp4r/feature_negotiation/iq/feature.rb FeatureNegotiation lib/xmpp4r/caps/helper/helper.rb lib/xmpp4r/caps/c.rb Caps lib/xmpp4r/rpc/helper/server.rb lib/xmpp4r/rpc/helper/client.rb lib/xmpp4r/rpc/iq/rpc.rb RPC lib/xmpp4r/xhtml/html.rb XHTML lib/xmpp4r/muc/iq/mucadminitem.rb lib/xmpp4r/muc/x/muc.rb lib/xmpp4r/muc/item.rb lib/xmpp4r/muc/helper/simplemucclient.rb lib/xmpp4r/muc/iq/mucadmin.rb lib/xmpp4r/muc/helper/mucbrowser.rb lib/xmpp4r/muc/x/mucuseritem.rb lib/xmpp4r/muc/x/mucuserinvite.rb lib/xmpp4r/muc/iq/mucowner.rb lib/xmpp4r/muc/helper/mucclient.rb MUC lib/xmpp4r/delay/x/delay.rb Delay lib/xmpp4r/pubsub/children/item.rb lib/xmpp4r/pubsub/children/configuration.rb lib/xmpp4r/pubsub/children/subscription.rb lib/xmpp4r/pubsub/helper/servicehelper.rb lib/xmpp4r/pubsub/children/unsubscribe.rb lib/xmpp4r/pubsub/children/publish.rb lib/xmpp4r/pubsub/children/event.rb lib/xmpp4r/pubsub/iq/pubsub.rb lib/xmpp4r/pubsub/helper/nodebrowser.rb lib/xmpp4r/pubsub/helper/nodehelper.rb lib/xmpp4r/pubsub/children/items.rb lib/xmpp4r/pubsub/children/subscription_config.rb lib/xmpp4r/pubsub/children/node_config.rb PubSub lib/xmpp4r/sasl.rb SASL lib/xmpp4r/httpbinding/client.rb HTTPBinding lib/xmpp4r/tune/helper/helper.rb lib/xmpp4r/tune/tune.rb UserTune lib/xmpp4r/dataforms/x/data.rb Dataforms lib/xmpp4r/bytestreams/helper/filetransfer.rb TransferSource FileTransfer lib/xmpp4r/discovery/iq/discoinfo.rb lib/xmpp4r/discovery/helper/responder.rb lib/xmpp4r/discovery/iq/discoitems.rb Discovery lib/xmpp4r/framework/base.rb lib/xmpp4r/framework/bot.rb Framework lib/xmpp4r/vcard/helper/vcard.rb lib/xmpp4r/vcard/iq/vcard.rb Vcard Jabber dot/m_99_0.png

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.

Methods

Classes and Modules

Class Jabber::Roster::Helper::RosterItem

Attributes

items  [R]  All items in your roster
items:[Hash] ([JID] => [Roster::Helper::RosterItem])

Public Class methods

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!

[Source]

    # File lib/xmpp4r/roster/helper/roster.rb, line 39
39:       def initialize(stream)
40:         @stream = stream
41:         @items = {}
42:         @items_lock = Mutex.new
43:         @roster_wait = Semaphore.new
44:         @query_cbs = CallbackList.new
45:         @update_cbs = CallbackList.new
46:         @presence_cbs = CallbackList.new
47:         @subscription_cbs = CallbackList.new
48:         @subscription_request_cbs = CallbackList.new
49: 
50:         # Register cbs
51:         stream.add_iq_callback(120, self) { |iq|
52:           if iq.query.kind_of?(IqQueryRoster)
53:             Thread.new do
54:               Thread.current.abort_on_exception = true
55:               handle_iq_query_roster(iq)
56:             end
57: 
58:             true
59:           else
60:             false
61:           end
62:         }
63:         stream.add_presence_callback(120, self) { |pres|
64:           Thread.new do
65:             Thread.current.abort_on_exception = true
66:             handle_presence(pres)
67:           end
68:         }
69: 
70:         # Request the roster
71:         rosterget = Iq.new_rosterget
72:         stream.send(rosterget)
73:       end

Public Instance methods

Get an item by jid

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

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 244
244:       def [](jid)
245:         jid = JID.new(jid) unless jid.kind_of? JID
246: 
247:         @items_lock.synchronize {
248:           if @items.has_key?(jid)
249:             @items[jid]
250:           elsif @items.has_key?(jid.strip)
251:             @items[jid.strip]
252:           else
253:             nil
254:           end
255:         }
256:       end

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

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 347
347:       def accept_subscription(jid, iname=nil)
348:         pres = Presence.new.set_type(:subscribed).set_to(jid.strip)
349:         @stream.send(pres)
350: 
351:         unless self[jid.strip]
352:           request = Iq.new_rosterset
353:           request.query.add(Jabber::Roster::RosterItem.new(jid.strip, iname))
354:           @stream.send_with_id(request)
355:         end
356:       end

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

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 323
323:       def add(jid, iname=nil, subscribe=false)
324:         if self[jid]
325:           self[jid].send
326:         else
327:           request = Iq.new_rosterset
328:           request.query.add(Jabber::Roster::RosterItem.new(jid, iname))
329:           @stream.send_with_id(request)
330:           # Adding to list is handled by handle_iq_query_roster
331:         end
332: 
333:         if subscribe
334:           # Actually the item *should* already be known now,
335:           # but we do it manually to exclude conditions.
336:           pres = Presence.new.set_type(:subscribe).set_to(jid.strip)
337:           @stream.send(pres)
338:         end
339:       end

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:

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 116
116:       def add_presence_callback(prio = 0, ref = nil, &block)
117:         @presence_cbs.add(prio, ref, block)
118:       end

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

[Source]

    # File lib/xmpp4r/roster/helper/roster.rb, line 89
89:       def add_query_callback(prio = 0, ref = nil, &block)
90:         @query_cbs.add(prio, ref, block)
91:       end

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:

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 131
131:       def add_subscription_callback(prio = 0, ref = nil, &block)
132:         @subscription_cbs.add(prio, ref, block)
133:       end

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

The block receives two objects:

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

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 154
154:       def add_subscription_request_callback(prio = 0, ref = nil, &block)
155:         @subscription_request_cbs.add(prio, ref, block)
156:       end

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:

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 102
102:       def add_update_callback(prio = 0, ref = nil, &block)
103:         @update_cbs.add(prio, ref, block)
104:       end

Decline a subscription request

  • Sends a <presence type=‘unsubscribed’/> stanza

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 361
361:       def decline_subscription(jid)
362:         pres = Presence.new.set_type(:unsubscribed).set_to(jid.strip)
363:         @stream.send(pres)
364:       end

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

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 261
261:       def find(jid)
262:         jid = JID.new(jid) unless jid.kind_of? JID
263: 
264:         j = jid.strip
265:         l = {}
266:         @items_lock.synchronize {
267:           @items.each_pair do |k, v|
268:             l[k] = v if k.strip == j
269:           end
270:         }
271:         l
272:       end

Get items in a group

When group is nil, return ungrouped items

group:[String] Group name
result:Array of [RosterItem]

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 297
297:       def find_by_group(group)
298:         res = []
299:         @items_lock.synchronize {
300:           @items.each_pair do |jid,item|
301:             res.push(item) if item.groups.include?(group)
302:             res.push(item) if item.groups == [] and group.nil?
303:           end
304:         }
305:         res
306:       end

Groups in this Roster, sorted by name

Contains nil if there are ungrouped items

result:[Array] containing group names (String)

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 280
280:       def groups
281:         res = []
282:         @items_lock.synchronize {
283:           @items.each_pair do |jid,item|
284:             res += item.groups
285:             res += [nil] if item.groups == []
286:           end
287:         }
288:         res.uniq.sort { |a,b| a.to_s <=> b.to_s }
289:       end

Wait for first roster query result to arrive

[Source]

    # File lib/xmpp4r/roster/helper/roster.rb, line 77
77:       def wait_for_roster
78:         @roster_wait.wait
79:         @roster_wait.run
80:       end

Private Instance methods

Handle received <iq/> stanzas, used internally

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 163
163:       def handle_iq_query_roster(iq)
164:         # If the <iq/> contains <error/> we just ignore that
165:         # and assume an empty roster
166:         iq.query.each_element('item') do |item|
167:           olditem, newitem = nil, nil
168: 
169:           @items_lock.synchronize {
170:             olditem = @items[item.jid]
171: 
172:             # Handle deletion of item
173:             if item.subscription == :remove
174:               @items.delete(item.jid)
175:             else
176:               newitem = @items[item.jid] = RosterItem.new(@stream).import(item)
177:             end
178:           }
179:           @update_cbs.process(olditem, newitem)
180:         end
181: 
182:         @roster_wait.run
183:         @query_cbs.process(iq)
184:       end

Handle received <presence/> stanzas, used internally

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 189
189:       def handle_presence(pres)
190:         item = self[pres.from]
191: 
192:         if [:subscribed, :unsubscribe, :unsubscribed].include?(pres.type)
193:           @subscription_cbs.process(item, pres)
194:           true
195: 
196:         elsif pres.type == :subscribe
197:           @subscription_request_cbs.process(item, pres)
198:           true
199: 
200:         else
201:           unless item.nil?
202:             update_presence(item, pres)
203:             true  # Callback consumed stanza
204:           else
205:             false # Callback did not consume stanza
206:           end
207:         end
208:       end

Update the presence of an item, used internally

Callbacks are called here

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 215
215:       def update_presence(item, pres)
216: 
217:         # This requires special handling, to announce all resources offline
218:         if pres.from.resource.nil? and pres.type == :error
219:           oldpresences = []
220:           item.each_presence do |oldpres|
221:             oldpresences << oldpres
222:           end
223: 
224:           item.add_presence(pres)
225:           oldpresences.each { |oldpres|
226:             @presence_cbs.process(item, oldpres, pres)
227:           }
228:         else
229:           oldpres = item.presence(pres.from).nil? ?
230:             nil :
231:             Presence.new.import(item.presence(pres.from))
232: 
233:           item.add_presence(pres)
234:           @presence_cbs.process(item, oldpres, pres)
235:         end
236:       end

[Validate]