Class Jabber::Roster::Helper
In: lib/xmpp4r/roster/helper/roster.rb
Parent: Object
Message Presence XMPPStanza Iq Singleton IdGenerator XMPPElement X IqQuery Error Connection Client Component Client Comparable JID RuntimeError AuthenticationFailure ErrorException NoNameXmlnsRegistered SOCKS5Error REXML::Element Stream SOCKS5Bytestreams SOCKS5BytestreamsTarget SOCKS5BytestreamsInitiator XMPPElement StreamHost IqSiFileRange IqSiFile StreamHostUsed IqSi XRosterItem RosterItem IqFeature XMUCUserItem XMUCUserInvite IqPubSub Items Subscription IqPubSubOwner Item Event Feature Item Identity XDataField XDataReported XDataTitle XDataInstructions IqVcard SOCKS5BytestreamsServerStreamHost TCPSocket SOCKS5Socket IqQuery IqQueryBytestreams IqQueryVersion IqQueryRoster IqQueryMUCOwner IqQueryRPC IqQueryDiscoItems IqQueryDiscoInfo IBB IBBTarget IBBInitiator Responder SimpleResponder Iq IqCommand RosterXItem XRoster RosterX X XMUCUser XMUC XDelay XData XParent MUCClient SimpleMUCClient XMLRPC::ParserWriterChooseMixin Client Server XMLRPC::ParseContentType XMLRPC::BasicServer Base DigestMD5 Plain ServiceHelper NodeHelper FileSource CallbackList Callback Semaphore StreamParser SOCKS5BytestreamsPeer SOCKS5BytestreamsServer IBBQueueItem Responder Helper MUCBrowser NodeBrowser Helper Helper lib/xmpp4r/authenticationfailure.rb lib/xmpp4r/xmppstanza.rb lib/xmpp4r/callbacks.rb lib/xmpp4r/idgenerator.rb lib/xmpp4r/connection.rb lib/xmpp4r/iq.rb lib/xmpp4r/jid.rb lib/xmpp4r/errorexception.rb lib/xmpp4r/client.rb lib/xmpp4r/stream.rb lib/xmpp4r/semaphore.rb lib/xmpp4r/streamparser.rb lib/xmpp4r/x.rb lib/xmpp4r/error.rb lib/xmpp4r/component.rb lib/xmpp4r/query.rb lib/xmpp4r/xmppelement.rb lib/xmpp4r/message.rb lib/xmpp4r/presence.rb lib/xmpp4r/bytestreams/helper/ibb/initiator.rb lib/xmpp4r/bytestreams/iq/si.rb lib/xmpp4r/bytestreams/iq/bytestreams.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb lib/xmpp4r/bytestreams/helper/ibb/base.rb lib/xmpp4r/bytestreams/helper/ibb/target.rb Bytestreams XParent lib/xmpp4r/version/iq/version.rb lib/xmpp4r/version/helper/responder.rb lib/xmpp4r/version/helper/simpleresponder.rb Version lib/xmpp4r/command/iq/command.rb lib/xmpp4r/command/helper/responder.rb Command lib/xmpp4r/roster/helper/roster.rb lib/xmpp4r/roster/iq/roster.rb lib/xmpp4r/roster/x/roster.rb Roster lib/xmpp4r/feature_negotiation/iq/feature.rb FeatureNegotiation lib/xmpp4r/muc/x/muc.rb lib/xmpp4r/muc/helper/mucclient.rb lib/xmpp4r/muc/x/mucuseritem.rb lib/xmpp4r/muc/helper/mucbrowser.rb lib/xmpp4r/muc/x/mucuserinvite.rb lib/xmpp4r/muc/iq/mucowner.rb lib/xmpp4r/muc/helper/simplemucclient.rb MUC lib/xmpp4r/rpc/helper/server.rb lib/xmpp4r/rpc/helper/client.rb lib/xmpp4r/rpc/iq/rpc.rb RPC lib/xmpp4r/sasl.rb SASL lib/xmpp4r/delay/x/delay.rb Delay lib/xmpp4r/pubsub/stanzas/subscription.rb lib/xmpp4r/pubsub/helper/servicehelper.rb lib/xmpp4r/pubsub/stanzas/item.rb lib/xmpp4r/pubsub/helper/nodehelper.rb lib/xmpp4r/pubsub/iq/pubsub.rb lib/xmpp4r/pubsub/stanzas/event.rb lib/xmpp4r/pubsub/helper/nodebrowser.rb lib/xmpp4r/pubsub/stanzas/items.rb PubSub lib/xmpp4r/httpbinding/client.rb HTTPBinding lib/xmpp4r/bytestreams/helper/filetransfer.rb TransferSource FileTransfer lib/xmpp4r/discovery/iq/discoinfo.rb lib/xmpp4r/discovery/iq/discoitems.rb Discovery lib/xmpp4r/dataforms/x/data.rb Dataforms lib/xmpp4r/vcard/helper/vcard.rb lib/xmpp4r/vcard/iq/vcard.rb Vcard Jabber dot/m_80_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 add_query_callback to get notifyed when Roster::Helper#items has been filled.

[Source]

    # File lib/xmpp4r/roster/helper/roster.rb, line 36
36:       def initialize(stream)
37:         @stream = stream
38:         @items = {}
39:         @items_lock = Mutex.new
40:         @query_cbs = CallbackList.new
41:         @update_cbs = CallbackList.new
42:         @presence_cbs = CallbackList.new
43:         @subscription_cbs = CallbackList.new
44:         @subscription_request_cbs = CallbackList.new
45: 
46:         # Register cbs
47:         stream.add_iq_callback(120, self) { |iq|
48:           if iq.query.kind_of?(IqQueryRoster)
49:             Thread.new do
50:               Thread.current.abort_on_exception = true
51:               handle_iq_query_roster(iq)
52:             end
53: 
54:             true
55:           else
56:             false
57:           end
58:         }
59:         stream.add_presence_callback(120, self) { |pres|
60:           Thread.new do
61:             Thread.current.abort_on_exception = true
62:             handle_presence(pres)
63:           end
64:         }
65: 
66:         # Request the roster
67:         rosterget = Iq.new_rosterget
68:         stream.send(rosterget)
69:       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 232
232:       def [](jid)
233:         jid = JID.new(jid) unless jid.kind_of? JID
234: 
235:         @items_lock.synchronize {
236:           if @items.has_key?(jid)
237:             @items[jid]
238:           elsif @items.has_key?(jid.strip)
239:             @items[jid.strip]
240:           else
241:             nil
242:           end
243:         }
244:       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 335
335:       def accept_subscription(jid, iname=nil)
336:         pres = Presence.new.set_type(:subscribed).set_to(jid.strip)
337:         @stream.send(pres)
338: 
339:         unless self[jid.strip]
340:           request = Iq.new_rosterset
341:           request.query.add(Jabber::Roster::RosterItem.new(jid.strip, iname))
342:           @stream.send_with_id(request) { true }
343:         end
344:       end

Add a user to your roster

Threading is encouraged as the function waits for a result. ErrorException 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 311
311:       def add(jid, iname=nil, subscribe=false)
312:         if self[jid]
313:           self[jid].send
314:         else
315:           request = Iq.new_rosterset
316:           request.query.add(Jabber::Roster::RosterItem.new(jid, iname))
317:           @stream.send_with_id(request) { true }
318:           # Adding to list is handled by handle_iq_query_roster
319:         end
320: 
321:         if subscribe
322:           # Actually the item *should* already be known now,
323:           # but we do it manually to exclude conditions.
324:           pres = Presence.new.set_type(:subscribe).set_to(jid.strip)
325:           @stream.send(pres)
326:         end
327:       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 105
105:       def add_presence_callback(prio = 0, ref = nil, &block)
106:         @presence_cbs.add(prio, ref, block)
107:       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 78
78:       def add_query_callback(prio = 0, ref = nil, &block)
79:         @query_cbs.add(prio, ref, block)
80:       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 120
120:       def add_subscription_callback(prio = 0, ref = nil, &block)
121:         @subscription_cbs.add(prio, ref, block)
122:       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 143
143:       def add_subscription_request_callback(prio = 0, ref = nil, &block)
144:         @subscription_request_cbs.add(prio, ref, block)
145:       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 91
91:       def add_update_callback(prio = 0, ref = nil, &block)
92:         @update_cbs.add(prio, ref, block)
93:       end

Decline a subscription request

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

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 349
349:       def decline_subscription(jid)
350:         pres = Presence.new.set_type(:unsubscribed).set_to(jid.strip)
351:         @stream.send(pres)
352:       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 249
249:       def find(jid)
250:         jid = JID.new(jid) unless jid.kind_of? JID
251: 
252:         j = jid.strip
253:         l = {}
254:         @items_lock.synchronize {
255:           @items.each_pair do |k, v|
256:             l[k] = v if k.strip == j
257:           end
258:         }
259:         l
260:       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 285
285:       def find_by_group(group)
286:         res = []
287:         @items_lock.synchronize {
288:           @items.each_pair do |jid,item|
289:             res.push(item) if item.groups.include?(group)
290:             res.push(item) if item.groups == [] and group.nil?
291:           end
292:         }
293:         res
294:       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 268
268:       def groups
269:         res = []
270:         @items_lock.synchronize {
271:           @items.each_pair do |jid,item|
272:             res += item.groups
273:             res += [nil] if item.groups == []
274:           end
275:         }
276:         res.uniq.sort { |a,b| a.to_s <=> b.to_s }
277:       end

Private Instance methods

Handle received <iq/> stanzas, used internally

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 152
152:       def handle_iq_query_roster(iq)
153:         # If the <iq/> contains <error/> we just ignore that
154:         # and assume an empty roster
155:         iq.query.each_element('item') do |item|
156:           olditem, newitem = nil, nil
157: 
158:           @items_lock.synchronize {
159:             olditem = @items[item.jid]
160: 
161:             # Handle deletion of item
162:             if item.subscription == :remove
163:               @items.delete(item.jid)
164:             else
165:               newitem = @items[item.jid] = RosterItem.new(@stream).import(item)
166:             end
167:           }
168:           @update_cbs.process(olditem, newitem)
169:         end
170:           
171:         @query_cbs.process(iq)
172:       end

Handle received <presence/> stanzas, used internally

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 177
177:       def handle_presence(pres)
178:         item = self[pres.from]
179: 
180:         if [:subscribed, :unsubscribe, :unsubscribed].include?(pres.type)
181:           @subscription_cbs.process(item, pres)
182:           true
183: 
184:         elsif pres.type == :subscribe
185:           @subscription_request_cbs.process(item, pres)
186:           true
187: 
188:         else
189:           unless item.nil?
190:             update_presence(item, pres)
191:             true  # Callback consumed stanza
192:           else
193:             false # Callback did not consume stanza
194:           end
195:         end
196:       end

Update the presence of an item, used internally

Callbacks are called here

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 203
203:       def update_presence(item, pres)
204: 
205:         # This requires special handling, to announce all resources offline
206:         if pres.from.resource.nil? and pres.type == :error
207:           oldpresences = []
208:           item.each_presence do |oldpres|
209:             oldpresences << oldpres
210:           end
211: 
212:           item.add_presence(pres)
213:           oldpresences.each { |oldpres|
214:             @presence_cbs.process(item, oldpres, pres)
215:           }
216:         else
217:           oldpres = item.presence(pres.from).nil? ?
218:             nil :
219:             Presence.new.import(item.presence(pres.from))
220: 
221:           item.add_presence(pres)
222:           @presence_cbs.process(item, oldpres, pres)
223:         end
224:       end

[Validate]