Class Jabber::Roster::Helper
In: lib/xmpp4r/roster/helper/roster.rb
Parent: Object
Message Presence XMPPStanza Iq XMPPElement ErrorResponse X IqQuery JabberError ComponentAuthenticationFailure ArgumentError InvalidChatState SOCKS5Error ServerError NoNameXmlnsRegistered ServerDisconnected ClientAuthenticationFailure Connection Client Component Connection Client Singleton IdGenerator Comparable JID Enumerable CallbackList Items Publish StandardError REXML::Element Stream XMPPElement Location IqFeature StreamHost IqSiFile IqSiFileRange IqSi StreamHostUsed XRosterItem RosterItem C Body HTML UserItem XMUCUserInvite Configuration Retract IqPubSub Item IqPubSubOwner Event Subscription Unsubscribe Tune XDataField XDataReported XDataTitle XDataInstructions Feature Item Identity IqVcard PubSub::ServiceHelper Helper Helper SOCKS5Bytestreams SOCKS5BytestreamsTarget SOCKS5BytestreamsInitiator SOCKS5BytestreamsServerStreamHost TCPSocket SOCKS5Socket IqQuery IqQueryBytestreams IqQueryRoster IqQueryVersion IqQueryRPC IqQueryMUCOwner IqQueryMUCAdmin IqQueryDiscoItems IqQueryDiscoInfo IqQueryLastActivity IBB IBBTarget IBBInitiator RosterXItem XRoster RosterX X XDelay XMUC XMUCUser XData Responder SimpleResponder Iq IqCommand XMLRPC::ParserWriterChooseMixin Client Server XMLRPC::ParseContentType XMLRPC::BasicServer MUCClient SimpleMUCClient MUC::UserItem XMUCUserItem IqQueryMUCAdminItem XParent SubscriptionConfig NodeConfig OwnerNodeConfig EventItems ServiceHelper OAuthServiceHelper NodeHelper EventItem Base Anonymous DigestMD5 Plain FileSource Base Bot Callback StreamParser Semaphore SOCKS5BytestreamsPeer SOCKS5BytestreamsServer IBBQueueItem Helper Responder Helper Listener MUCBrowser NodeBrowser ListenerMocker Helper Responder Helper Helper 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 XParent lib/xmpp4r/location/helper/helper.rb lib/xmpp4r/location/location.rb UserLocation lib/xmpp4r/feature_negotiation/iq/feature.rb FeatureNegotiation 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/roster/x/roster.rb lib/xmpp4r/roster/helper/roster.rb lib/xmpp4r/roster/iq/roster.rb Roster lib/xmpp4r/version/helper/responder.rb lib/xmpp4r/version/helper/simpleresponder.rb lib/xmpp4r/version/iq/version.rb Version lib/xmpp4r/command/iq/command.rb lib/xmpp4r/command/helper/responder.rb Command lib/xmpp4r/caps/helper/helper.rb lib/xmpp4r/caps/c.rb Caps lib/xmpp4r/reliable.rb Reliable lib/xmpp4r/delay/x/delay.rb Delay lib/xmpp4r/xhtml/html.rb XHTML lib/xmpp4r/rpc/helper/server.rb lib/xmpp4r/rpc/helper/client.rb lib/xmpp4r/rpc/iq/rpc.rb RPC 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/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/helper/oauth_service_helper.rb lib/xmpp4r/pubsub/children/event.rb lib/xmpp4r/pubsub/iq/pubsub.rb lib/xmpp4r/pubsub/children/retract.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 OAuthPubSubStreamHelper PubSub lib/xmpp4r/httpbinding/client.rb HTTPBinding lib/xmpp4r/tune/helper/helper.rb lib/xmpp4r/tune/tune.rb UserTune lib/xmpp4r/sasl.rb SASL lib/xmpp4r/test/listener_mocker.rb Test lib/xmpp4r/dataforms/x/data.rb Dataforms lib/xmpp4r/discovery/helper/helper.rb lib/xmpp4r/discovery/iq/discoinfo.rb lib/xmpp4r/discovery/helper/responder.rb lib/xmpp4r/discovery/iq/discoitems.rb Discovery lib/xmpp4r/bytestreams/helper/filetransfer.rb TransferSource FileTransfer lib/xmpp4r/last/helper/helper.rb lib/xmpp4r/last/iq/last.rb LastActivity 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_110_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, startnow = true)
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:         get_roster if startnow
70:       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 247
247:       def [](jid)
248:         jid = JID.new(jid) unless jid.kind_of? JID
249: 
250:         @items_lock.synchronize {
251:           if @items.has_key?(jid)
252:             @items[jid]
253:           elsif @items.has_key?(jid.strip)
254:             @items[jid.strip]
255:           else
256:             nil
257:           end
258:         }
259:       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 350
350:       def accept_subscription(jid, iname=nil)
351:         pres = Presence.new.set_type(:subscribed).set_to(jid.strip)
352:         @stream.send(pres)
353: 
354:         unless self[jid.strip]
355:           request = Iq.new_rosterset
356:           request.query.add(Jabber::Roster::RosterItem.new(jid.strip, iname))
357:           @stream.send_with_id(request)
358:         end
359:       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 326
326:       def add(jid, iname=nil, subscribe=false)
327:         if self[jid]
328:           self[jid].send
329:         else
330:           request = Iq.new_rosterset
331:           request.query.add(Jabber::Roster::RosterItem.new(jid, iname))
332:           @stream.send_with_id(request)
333:           # Adding to list is handled by handle_iq_query_roster
334:         end
335: 
336:         if subscribe
337:           # Actually the item *should* already be known now,
338:           # but we do it manually to exclude conditions.
339:           pres = Presence.new.set_type(:subscribe).set_to(jid.strip)
340:           @stream.send(pres)
341:         end
342:       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 119
119:       def add_presence_callback(prio = 0, ref = nil, &block)
120:         @presence_cbs.add(prio, ref, block)
121:       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 92
92:       def add_query_callback(prio = 0, ref = nil, &block)
93:         @query_cbs.add(prio, ref, block)
94:       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 134
134:       def add_subscription_callback(prio = 0, ref = nil, &block)
135:         @subscription_cbs.add(prio, ref, block)
136:       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 157
157:       def add_subscription_request_callback(prio = 0, ref = nil, &block)
158:         @subscription_request_cbs.add(prio, ref, block)
159:       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 105
105:       def add_update_callback(prio = 0, ref = nil, &block)
106:         @update_cbs.add(prio, ref, block)
107:       end

Decline a subscription request

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

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 364
364:       def decline_subscription(jid)
365:         pres = Presence.new.set_type(:unsubscribed).set_to(jid.strip)
366:         @stream.send(pres)
367:       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 264
264:       def find(jid)
265:         jid = JID.new(jid) unless jid.kind_of? JID
266: 
267:         j = jid.strip
268:         l = {}
269:         @items_lock.synchronize {
270:           @items.each_pair do |k, v|
271:             l[k] = v if k.strip == j
272:           end
273:         }
274:         l
275:       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 300
300:       def find_by_group(group)
301:         res = []
302:         @items_lock.synchronize {
303:           @items.each_pair do |jid,item|
304:             res.push(item) if item.groups.include?(group)
305:             res.push(item) if item.groups == [] and group.nil?
306:           end
307:         }
308:         res
309:       end

[Source]

    # File lib/xmpp4r/roster/helper/roster.rb, line 72
72:       def get_roster
73:         # Request the roster
74:         rosterget = Iq.new_rosterget
75:         @stream.send(rosterget)
76:       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 283
283:       def groups
284:         res = []
285:         @items_lock.synchronize {
286:           @items.each_pair do |jid,item|
287:             res += item.groups
288:             res += [nil] if item.groups == []
289:           end
290:         }
291:         res.uniq.sort { |a,b| a.to_s <=> b.to_s }
292:       end

Wait for first roster query result to arrive

[Source]

    # File lib/xmpp4r/roster/helper/roster.rb, line 80
80:       def wait_for_roster
81:         @roster_wait.wait
82:         @roster_wait.run
83:       end

Private Instance methods

Handle received <iq/> stanzas, used internally

[Source]

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

Handle received <presence/> stanzas, used internally

[Source]

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

Update the presence of an item, used internally

Callbacks are called here

[Source]

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

[Validate]