jabber4r 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,16 @@
1
+ if RUBY_VERSION=="1.8.0"
2
+ module REXML
3
+ module Parsers
4
+ class BaseParser
5
+ # Returns true if there are more events. Synonymous with !empty?
6
+ def has_next?
7
+ return true if @closed # THIS WAS ADDED TO FIX PROBLEM
8
+ @source.read if @source.buffer.size==0 and !@source.empty?
9
+ (!@source.empty? and @source.buffer.strip.size>0) or @stack.size>0 or @closed
10
+ end
11
+ end
12
+ end
13
+ end
14
+ else
15
+ puts "WARNING: rexml_1.8_patch is needed on Ruby 1.8.0 and not by Ruby #{RUBY_VERSION}"
16
+ end
@@ -0,0 +1,322 @@
1
+ # License: see LICENSE.txt
2
+ # Jabber4R - Jabber Instant Messaging Library for Ruby
3
+ # Copyright (C) 2002 Rich Kilmer <rich@infoether.com>
4
+ #
5
+
6
+
7
+ module Jabber
8
+
9
+ ##
10
+ # The Roster class encapsulates the runtime roster of the session instance.
11
+ # The Roster contains all subscriptions in a Jabber::Roster::RosterItem hash.
12
+ #
13
+ class Roster
14
+ ITEM_ADDED=1
15
+ ITEM_DELETED=2
16
+ RESOURCE_ADDED=4
17
+ RESOURCE_UPDATED=8
18
+ RESOURCE_DELETED=16
19
+
20
+ # The Jabber::Session instance
21
+ attr_reader :session
22
+
23
+ ##
24
+ # Creates a Roster for the session
25
+ #
26
+ # session:: [Jabber::Session] The session instance
27
+ #
28
+ def initialize(session)
29
+ @session = session
30
+ @map = {}
31
+ @listeners = {}
32
+ end
33
+
34
+ ##
35
+ # The RosterItem class embodies another Jabber user's status (from
36
+ # the local user's perspective). RosterItems contain
37
+ # Jabber::Roster::RosterItem::Resource objects for each resource
38
+ # location a foreign user is accessing through.
39
+ #
40
+ class RosterItem
41
+ # The Jabber::Roster instance
42
+ attr_reader :roster
43
+
44
+ # The Jabber ID (Jabber::JID)
45
+ attr_accessor :jid
46
+
47
+ # The subscription type
48
+ attr_accessor :subscription
49
+
50
+ # The (nick)name of this account
51
+ attr_accessor :name
52
+
53
+ # The group name for this account
54
+ attr_accessor :group
55
+
56
+ ##
57
+ # Constructs a RosterItem
58
+ #
59
+ # roster:: [Jabber::Roster] The roster instance
60
+ # subscription:: [String] The subscription type
61
+ # name:: [String] The (nick)name
62
+ # group:: [String=nil] The group this account belongs to
63
+ #
64
+ def initialize(roster, jid, subscription, name, group=nil)
65
+ @jid = jid
66
+ @subscription = subscription
67
+ @name = name
68
+ @group = group if group
69
+ @resources = {}
70
+ @roster = roster
71
+ end
72
+
73
+ ##
74
+ # The Resource class embodies a Resource endpoint in Jabber.
75
+ # The resource endpoint it what maintains a status (not an account).
76
+ #
77
+ class Resource
78
+
79
+ # The name of the resource
80
+ attr_reader :name
81
+
82
+ # How the resource should be shown
83
+ attr_reader :show
84
+
85
+ # The status message of the resource
86
+ attr_reader :status
87
+
88
+ ##
89
+ # Constructs a new Resource instance
90
+ #
91
+ # item:: [Jabber::Roster::RosterItem] The roster item this resource belongs to
92
+ # name:: [String] The resource name
93
+ # show:: [String] How the resource should be shown
94
+ # status:: [String] The status message of the resource
95
+ #
96
+ def initialize(item, name, show, status)
97
+ @item = item
98
+ @name = name
99
+ @show = show
100
+ @status = status
101
+ end
102
+
103
+ ##
104
+ # Updates the state of a resource and notifies listeners.
105
+ #
106
+ # show:: [String] How the resource should be shown
107
+ # status:: [String] The status message of the resource
108
+ #
109
+ def update(show, status)
110
+ @show = show
111
+ @status = status
112
+ @item.roster.notify_listeners(RESOURCE_UPDATED, self)
113
+ end
114
+
115
+ ##
116
+ # Dumps the Resource as a string
117
+ #
118
+ # return:: [String] The resource encoded as a string.
119
+ #
120
+ def to_s
121
+ "RESOURCE:#{@name} SHOW:#{@show} STATUS:#{@status}"
122
+ end
123
+ end
124
+
125
+ ##
126
+ # Retrieves the VCard for this (RosterItem) account. This method
127
+ # blocks until the the vcard is returned.
128
+ #
129
+ # return:: [Jabber::VCard] The VCard object for this account
130
+ #
131
+ def get_vcard
132
+ ct = Thread.current
133
+ queryID = @roster.session.id
134
+ result = nil
135
+ @roster.session.connection.send(Jabber::Protocol::Iq.gen_vcard(self, queryID, jid)) { |je|
136
+ if je.element_tag == "iq" and je.attr_type=="result" and je.attr_id == queryID
137
+ je.consume_element
138
+ result = Jabber::VCard.from_element(je.VCARD)
139
+ ct.wakeup
140
+ else
141
+ end
142
+ }
143
+ Thread.stop
144
+ return result
145
+ end
146
+
147
+ ##
148
+ # Adds a new resource to the Roster item and notifies listeners
149
+ #
150
+ # resourceName:: [String] The name of the resource
151
+ # show:: [String] How the resource is to be viewed
152
+ # status:: [String] The status message
153
+ # return:: [Jabber::Roster:RosterItem::Resource] The new Resource instance
154
+ #
155
+ def add(resourceName, show, status)
156
+ resource = Resource.new(self, resourceName, show, status)
157
+ @resources[resourceName] = resource
158
+ @roster.notify_listeners(RESOURCE_ADDED, resource)
159
+ resource
160
+ end
161
+
162
+ ##
163
+ # Deletes a resource from this roster item and notifies listeners
164
+ #
165
+ # resourceName:: [String] The name of the resource
166
+ # return:: [Jabber::Roster:RosterItem::Resource] The deleted Resource
167
+ #
168
+ def delete(resourceName)
169
+ resource = @resources.delete(resourceName)
170
+ @roster.notify_listeners(RESOURCE_DELETED, resource) if resource
171
+ resource
172
+ end
173
+
174
+ ##
175
+ # Retrieves a resource object
176
+ #
177
+ # resourceName:: [String] The name of the resource
178
+ # return:: [Jabber::Roster:RosterItem::Resource] The Resource instance
179
+ #
180
+ def [](resourceName)
181
+ return @resources[resourceName]
182
+ end
183
+
184
+ ##
185
+ # Iterates over the list of available resources
186
+ #
187
+ # yield:: |Jabber::Roster:RosterItem::Resource| The resource instance
188
+ #
189
+ def each_resource
190
+ @resources.each_value {|resource| yield resource}
191
+ end
192
+
193
+ ##
194
+ # Dumps the roster item
195
+ #
196
+ # return:: [String] The roster item dumped as a String
197
+ def to_s
198
+ "ITEM:#{@jid.to_s} SUBSCRIPTION:#{@subscription} NAME:#{@name} GROUP:#{@group}"
199
+ end
200
+ end
201
+
202
+ ##
203
+ # Adds a listener to the roster to process roster changes
204
+ #
205
+ # &block:: [Block |event, rosteritem|] The block to process roster changes
206
+ # return:: [String] The listener id to use to deregister
207
+ #
208
+ def add_listener(&block)
209
+ id = Jabber.gen_random_id("", 10)
210
+ @listeners[id]=block if block
211
+ return id
212
+ end
213
+
214
+ ##
215
+ # Deletes a listener for processing roster messages
216
+ #
217
+ # id:: [String] A listener id (given by add_listener)
218
+ #
219
+ def delete_listener(id)
220
+ @listeners.delete(id)
221
+ end
222
+
223
+ ##
224
+ # Adds a subscription to be tracked in the Roster
225
+ #
226
+ # jid:: [JID | String] The Jabber ID
227
+ # subscription:: [String] The subscription type (both)
228
+ # name:: [String] The nickname
229
+ # group:: [String = nil] The name of the group of the roster item.
230
+ #
231
+ def add(jid, subscription, name, group=nil)
232
+ if jid.kind_of? String
233
+ jid = JID.new(jid)
234
+ jid.strip_resource
235
+ elsif jid.kind_of? JID
236
+ jid = JID.new(jid.node+"@"+jid.host)
237
+ else
238
+ return
239
+ end
240
+ begin
241
+ item = RosterItem.new(self, jid, subscription, name, group)
242
+ @map[jid.to_s] = item
243
+ notify_listeners(ITEM_ADDED, item)
244
+ rescue => ex
245
+ puts ex.backtrace.join("\n")
246
+ end
247
+ end
248
+
249
+ ##
250
+ # Returns a Jabber::Roster::RosterItem based on the JID
251
+ #
252
+ # jid:: [Jabber::JID | String] The Jabber ID
253
+ # return:: [Jabber::Roster::RosterItem] The roster item
254
+ #
255
+ def [](jid)
256
+ if jid.kind_of? String
257
+ jid = JID.new(jid)
258
+ jid.strip_resource
259
+ elsif jid.kind_of? JID
260
+ jid = JID.new(jid.node+"@"+jid.host)
261
+ else
262
+ return
263
+ end
264
+ return @map[jid.to_s]
265
+ end
266
+
267
+ ##
268
+ # Deletes a roster item based on the supplied Jabber ID
269
+ #
270
+ # jid:: [Jabber::JID | String]
271
+ #
272
+ def delete(jid)
273
+ if jid.kind_of? String
274
+ jid = JID.new(jid)
275
+ jid.strip_resource
276
+ elsif jid.kind_of? JID
277
+ jid = JID.new(jid.node+"@"+jid.host)
278
+ else
279
+ return
280
+ end
281
+ item = @map.delete(jid.to_s)
282
+ notify_listeners(ITEM_DELETED, item) if item
283
+ item
284
+ end
285
+
286
+ ##
287
+ # Iterates over each RosterItem
288
+ #
289
+ # yield:: [Jabber::Roster::RosterItem] The roster item.
290
+ #
291
+ def each_item
292
+ @map.each_value {|item| yield item}
293
+ end
294
+
295
+ ##
296
+ # Dumps the Roster state as a string
297
+ #
298
+ # return:: [String] The roster state
299
+ #
300
+ def to_s
301
+ result = "ROSTER DUMP\n"
302
+ each_item do |item|
303
+ result += (item.to_s+"\n")
304
+ item.each_resource {|resource| result+= " #{resource.to_s}\n"}
305
+ end
306
+ return result
307
+ end
308
+
309
+ ##
310
+ # Notifies listeners of a roster change event
311
+ #
312
+ # event:: [Integer] The roster event
313
+ # object:: [RosterItem] The modified item
314
+ #
315
+ def notify_listeners(event, object)
316
+ @listeners.each_value {|listener| listener.call(event, object)}
317
+ end
318
+
319
+ end
320
+
321
+ end
322
+
@@ -0,0 +1,615 @@
1
+ # License: see LICENSE.txt
2
+ # Jabber4R - Jabber Instant Messaging Library for Ruby
3
+ # Copyright (C) 2002 Rich Kilmer <rich@infoether.com>
4
+ #
5
+
6
+
7
+ module Jabber
8
+ HEX = "0123456789abcdef"
9
+
10
+ ##
11
+ # Generates a random hex string in the following format:
12
+ # JRR_01234567
13
+ #
14
+ # return:: [String] The resource id
15
+ #
16
+ def Jabber.gen_random_resource
17
+ return Jabber.gen_random_id("JRR_", 8)
18
+ end
19
+
20
+ ##
21
+ # Generates a random thread as a hex string in the following format:
22
+ # JRT_01234567890123456789
23
+ #
24
+ # return:: [String] The thread id
25
+ #
26
+ def Jabber.gen_random_thread
27
+ return Jabber.gen_random_id("JRT_", 20)
28
+ end
29
+
30
+ ##
31
+ # Generates a random id as a hex string
32
+ #
33
+ # prefix:: [String="Jabber4R_] The prefix for the random hex data
34
+ # length:: [Integer=16] The number of hex characters
35
+ # return:: [String] The random id
36
+ #
37
+ def Jabber.gen_random_id(prefix="Jabber4R_", length=16)
38
+ length.times {prefix += HEX[rand(16),1]}
39
+ prefix
40
+ end
41
+
42
+ class Subscription
43
+ attr_accessor :type, :from, :id, :session
44
+ def initialize(session, type, from, id)
45
+ @session = session
46
+ @type = type
47
+ @from = from
48
+ @id = id
49
+ end
50
+ def accept
51
+ case type
52
+ when :subscribe
53
+ @session.connection.send(Jabber::Protocol::Presence.gen_accept_subscription(@id, @from))
54
+ when :unsubscribe
55
+ @session.connection.send(Jabber::Protocol::Presence.gen_accept_unsubscription(@id, @from))
56
+ else
57
+ raise "Cannot accept a subscription of type #{type.to_s}"
58
+ end
59
+ end
60
+ end
61
+
62
+ ##
63
+ # This is a base class for subscription handlers
64
+
65
+ class SubscriptionHandler
66
+ def subscribe(subscription)
67
+ end
68
+
69
+ def subscribed(subscription)
70
+ end
71
+
72
+ def unsubscribe(subscription)
73
+ end
74
+
75
+ def unsubscribed(subscription)
76
+ end
77
+ end
78
+
79
+ class AutoSubscriptionHandler < SubscriptionHandler
80
+
81
+ def subscribe(subscription)
82
+ subscription.accept
83
+ end
84
+
85
+ def unsubscribe(subscription)
86
+ subscription.accept
87
+ end
88
+ end
89
+
90
+
91
+ ##
92
+ # The Jabber Session is the main class for dealing with a Jabber service.
93
+ #
94
+ class Session
95
+
96
+ # The host this session is connected to
97
+ attr_reader :host
98
+
99
+ # The port (defaults to 5222) that this session is connected to
100
+ attr_reader :port
101
+
102
+ # The Jabber::Protocol::Connection instance
103
+ attr_reader :connection
104
+
105
+ # The Jabber::Roster instance
106
+ attr_reader :roster
107
+
108
+ # The session id sent from the Jabber service upon connection
109
+ attr_reader :session_id
110
+
111
+ # The Jabber::JID of the current session
112
+ attr_reader :jid
113
+
114
+ # The username to use for authenticating this session
115
+ attr_accessor :username
116
+
117
+ # The password to use for authenticating this session
118
+ attr_accessor :password
119
+
120
+ # The resource id for this session
121
+ attr_accessor :resource
122
+
123
+ # The iq handlers for this session
124
+ attr_accessor :iqHandlers
125
+
126
+ ##
127
+ # Session creation factory that creates a session, logs in,
128
+ # requests the roster, registers message and presence filters
129
+ # and announces initial presence. Login is done via plaintext
130
+ # password authentication.
131
+ #
132
+ # jid:: [String | JID] The account information ("account@host/resouce")
133
+ # password:: [String] The account password
134
+ # port:: [Integer = 5222] The host port
135
+ # digest:: [Boolean = false] Use digest authentication?
136
+ # return:: [Jabber::Session] The new session
137
+ #
138
+ def Session.bind(jid, password, port=5222, digest=false)
139
+ jid = Jabber::JID.new(jid) if jid.kind_of? String
140
+ session = Session.new(jid.host, port)
141
+ raise "Authentication failed" unless session.authenticate(jid.node, password, jid.resource, digest)
142
+ session.request_roster
143
+ session.register_message_filter
144
+ session.register_presence_filter
145
+ session.register_iq_filter
146
+ session.announce_initial_presence
147
+ session
148
+ end
149
+
150
+ ##
151
+ # Account registration method
152
+ #
153
+ def Session.register(jid, password, email="", name="", port=5222)
154
+ jid = Jabber::JID.new(jid) if jid.kind_of? String
155
+ session = Session.new(jid.host, port)
156
+ msg_id = session.id
157
+ registered = false
158
+ current = Thread.current
159
+ session.connection.send(Jabber::Protocol::Iq.gen_registration(session, msg_id, jid.node, password, email, name)) do |element|
160
+ if element.element_tag=="iq" and element.attr_id==msg_id
161
+ element.consume_element
162
+ if element.attr_type=="result"
163
+ registered = true
164
+ elsif element.attr_type=="error"
165
+ registered = false
166
+ end
167
+ current.wakeup
168
+ end
169
+ end
170
+ Thread.stop
171
+ session.release
172
+ return registered
173
+ end
174
+
175
+ ##
176
+ # Session creation factory that creates a session, logs in,
177
+ # requests the roster, registers message and presence filters
178
+ # and announces initial presence. Login is done via digest (SHA)
179
+ # password authentication.
180
+ #
181
+ # jid:: [String | JID] The account information ("account@host/resouce")
182
+ # password:: [String] The account password
183
+ # port:: [Integer = 5222] The host port
184
+ # return:: [Jabber::Session] The new session
185
+ #
186
+ def Session.bind_digest(jid, password, port=5222)
187
+ Session.bind(jid, password, port, true)
188
+ end
189
+
190
+ # Creates a new session connected to the supplied host and port.
191
+ # The method attempts to build a Jabber::Protocol::Connection
192
+ # object and send the open_stream XML message. It then blocks
193
+ # to recieve the coorisponding reply open_stream and sets the
194
+ # session_id from that xml element.
195
+ #
196
+ # host:: [String] The hostname of the Jabber service
197
+ # port:: [Integer=5222] The port of the Jabber service
198
+ # raise:: [RuntimeException] If connection fails
199
+ #
200
+ def initialize(host, port=5222)
201
+ @id = 1
202
+ @host = host
203
+ @port = port
204
+ @roster = Roster.new(self)
205
+ @messageListeners = Hash.new
206
+ @iqHandlers=Hash.new
207
+ @subscriptionHandler = nil
208
+ @connection = Jabber::Protocol::Connection.new(host, port)
209
+ @connection.connect
210
+ unless @connection.is_connected?
211
+ raise "Session Error: Could not connected to #{host}:#{port}"
212
+ else
213
+ @connection.send(Jabber::Protocol.gen_open_stream(host)) do |element|
214
+ if element.element_tag=="stream:stream"
215
+ element.consume_element
216
+ @session_id = element.attr_id
217
+ end
218
+ end
219
+ @connection.on_connection_exception do
220
+ if @session_failure_block
221
+ self.release
222
+ @session_failure_block.call
223
+ end
224
+ end
225
+ Thread.stop
226
+ end
227
+ end
228
+
229
+ ##
230
+ # Set a handler for session exceptions that get caught in
231
+ # communicating with the Jabber server.
232
+ #
233
+ def on_session_failure(&block)
234
+ @session_failure_block = block
235
+ end
236
+
237
+ ##
238
+ # Counter for message IDs
239
+ #
240
+ # return:: [String] A unique message id for this session
241
+ #
242
+ def id
243
+ @id = @id + 1
244
+ return @id.to_s
245
+ end
246
+
247
+ ##
248
+ # Authenticate (logs into) this session with the supplied credentials.
249
+ # The method blocks waiting for a reply to the login message. Sets the
250
+ # authenticated attribute based on result.
251
+ #
252
+ # username:: [String] The username to use for authentication
253
+ # password:: [String] The password to use for authentication
254
+ # resource:: [String] The resource ID for this session
255
+ # digest:: [Boolean=false] True to use digest authentication (not sending password in the clear)
256
+ # return:: [Boolean] Whether the authentication succeeded or failed
257
+ #
258
+ def authenticate(username, password, resource, digest=false)
259
+ @username = username
260
+ @password = password
261
+ @resource = resource
262
+ @jid = JID.new("#{username}@#{@host}/#{resource}")
263
+ @roster.add(@jid, "both", "Me", "My Resources")
264
+
265
+ msg_id = self.id
266
+ authHandler = Proc.new do |element|
267
+ if element.element_tag=="iq" and element.attr_id==msg_id
268
+ element.consume_element
269
+ if element.attr_type=="result"
270
+ @authenticated = true
271
+ elsif element.attr_type=="error"
272
+ @authenticated = false
273
+ end
274
+ end
275
+ end
276
+ if digest
277
+ require 'digest/sha1'
278
+ authRequest = Jabber::Protocol::Iq.gen_auth_digest(self, msg_id, username, Digest::SHA1.new(@session_id + password).hexdigest, resource)
279
+ else
280
+ authRequest = Jabber::Protocol::Iq.gen_auth(self, msg_id, username, password, resource)
281
+ end
282
+ @connection.send(authRequest, &authHandler)
283
+ Thread.stop
284
+ return @authenticated
285
+ end
286
+
287
+ ##
288
+ # Is this an authenticated session?
289
+ #
290
+ # return:: [Boolean] True if the session is authenticated
291
+ #
292
+ def is_authenticated?
293
+ return @authenticated
294
+ end
295
+
296
+ ##
297
+ # Sends the initial presence message to the Jabber service
298
+ #
299
+ def announce_initial_presence
300
+ @connection.send(Jabber::Protocol::Presence.gen_initial(id))
301
+ end
302
+
303
+ ##
304
+ # Sends an extended away presence message
305
+ #
306
+ # status:: [String] The status message
307
+ #
308
+ def announce_extended_away(status=nil)
309
+ @connection.send(Jabber::Protocol::Presence.gen_xa(id, status))
310
+ end
311
+
312
+ ##
313
+ # Sends a free for chat presence message
314
+ #
315
+ # status:: [String] The status message
316
+ #
317
+ def announce_free_for_chat(status=nil)
318
+ @connection.send(Jabber::Protocol::Presence.gen_chat(id, status))
319
+ end
320
+
321
+ ##
322
+ # Sends a 'normal' presence message
323
+ #
324
+ # status:: [String] The status message
325
+ #
326
+ def announce_normal(status=nil)
327
+ @connection.send(Jabber::Protocol::Presence.gen_normal(id, status))
328
+ end
329
+
330
+ ##
331
+ # Sends an away from computer presence message
332
+ #
333
+ # status:: [String] The status message
334
+ #
335
+ def announce_away_from_computer(status=nil)
336
+ @connection.send(Jabber::Protocol::Presence.gen_away(id, status))
337
+ end
338
+
339
+ ##
340
+ # Sends a do not disturb presence message
341
+ #
342
+ # status:: [String] The status message
343
+ #
344
+ def announce_do_not_disturb(status=nil)
345
+ @connection.send(Jabber::Protocol::Presence.gen_dnd(id, status))
346
+ end
347
+
348
+ ##
349
+ # Sets the handler for subscription requests, notifications, etc.
350
+ #
351
+ def set_subscription_handler(handler=nil, &block)
352
+ @subscriptionHandler = handler.new(self) if handler
353
+ @subscriptionHandler = block if block_given? and !handler
354
+ end
355
+
356
+ def enable_autosubscription
357
+ set_subscription_handler AutoSubscriptionHandler
358
+ end
359
+
360
+ def subscribe(to, name="")
361
+ to = JID.to_jid(to)
362
+ roster_item = @roster[to]
363
+
364
+ if roster_item #if you already have a roster item just send the subscribe request
365
+ if roster_item.subscription=="to" or roster_item.subscription=="both"
366
+ return
367
+ end
368
+ @connection.send(Jabber::Protocol::Presence.gen_new_subscription(to))
369
+ return
370
+ end
371
+ myid = self.id
372
+ @connection.send(Jabber::Protocol::Iq.gen_add_rosteritem(self, myid, to, name)) do |element|
373
+ if element.attr_id==myid
374
+ element.consume_element
375
+ if element.attr_type=="result"
376
+ @connection.send(Jabber::Protocol::Presence.gen_new_subscription(to))
377
+ end
378
+ end
379
+ end
380
+ end
381
+
382
+ ##
383
+ # Adds a filter to the Connection to manage tracking resources that come online/offline
384
+ #
385
+ def register_presence_filter
386
+ @connection.add_filter("presenceAvailableFilter") do |element|
387
+ if element.element_tag=="presence"
388
+ type = element.attr_type
389
+ type = nil if type.nil?
390
+ case type
391
+ when nil, "available"
392
+ element.consume_element
393
+ from = JID.new(element.attr_from)
394
+ rItem = @roster[from]
395
+ show = element.show.element_data
396
+ show = "chat" unless show
397
+ status = element.status.element_data
398
+ status = "" unless status
399
+ if rItem
400
+ resource = rItem[from.resource]
401
+ if resource
402
+ resource.update(show, status)
403
+ else
404
+ rItem.add(from.resource, show, status)
405
+ end
406
+ end
407
+ when "unavailable"
408
+ element.consume_element
409
+ from = JID.new(element.attr_from)
410
+ rItem = @roster[from]
411
+ resource = rItem.delete(from.resource) if rItem
412
+ when "subscribe", "unsubscribe", "subscribed", "unsubscribed"
413
+ element.consume_element
414
+ from = JID.new(element.attr_from)
415
+ break unless @subscriptionHandler
416
+ if @subscriptionHandler.kind_of? Proc
417
+ @subscriptionHandler.call(Subscription.new(self, type.intern, from, id))
418
+ else
419
+ @subscriptionHandler.send(Subscription.new(self, type.intern, from, id))
420
+ end
421
+ end
422
+ end #if presence
423
+ end #do
424
+ end
425
+
426
+ ##
427
+ # Creates a new message to the supplied JID of type NORMAL
428
+ #
429
+ # to:: [Jabber::JID] Who to send the message to
430
+ # type:: [String = Jabber::Protocol::Message::NORMAL] The type of message to send (see Jabber::Protocol::Message)
431
+ # return:: [Jabber::Protocol::Message] The new message
432
+ #
433
+ def new_message(to, type=Jabber::Protocol::Message::NORMAL)
434
+ msg = Jabber::Protocol::Message.new(to, type)
435
+ msg.session=self
436
+ return msg
437
+ end
438
+
439
+ ##
440
+ # Creates a new message addressed to the supplied JID of type CHAT
441
+ #
442
+ # to:: [JID] Who to send the message to
443
+ # return:: [Jabber::Protocol::Message] The new (chat) message
444
+ #
445
+ def new_chat_message(to)
446
+ self.new_message(to, Jabber::Protocol::Message::CHAT)
447
+ end
448
+
449
+ ##
450
+ # Creates a new message addressed to the supplied JID of type GROUPCHAT
451
+ #
452
+ # to:: [JID] Who to send the message to
453
+ # return:: [Jabber::Protocol::Message] The new (group chat) message
454
+ #
455
+ def new_group_chat_message(to)
456
+ self.new_message(to, Jabber::Protocol::Message::GROUPCHAT)
457
+ end
458
+
459
+ ##
460
+ # Adds a filter to the Connection to manage tracking messages to forward
461
+ # to registered message listeners.
462
+ #
463
+ def register_message_filter
464
+ @connection.add_filter("messageFilter") do |element|
465
+ if element.element_tag=="message" and @messageListeners.size > 0
466
+ element.consume_element
467
+ message = Jabber::Protocol::Message.from_element(self, element)
468
+ notify_message_listeners(message)
469
+ end #if message
470
+ end #do
471
+ end
472
+
473
+ ##
474
+ # Add a listener for new messages
475
+ #
476
+ # Usage::
477
+ # id = session.add_message_listener do |message|
478
+ # puts message
479
+ # end
480
+ #
481
+ # &block [Block] The block to process a message
482
+ # return:: [String] The listener ID...used to remove the listener
483
+ #
484
+ def add_message_listener(&block)
485
+ id = Jabber.gen_random_id("", 10)
486
+ @messageListeners[id]=block if block
487
+ return id
488
+ end
489
+
490
+ ##
491
+ # Deletes a message listener
492
+ #
493
+ # id:: [String] A messanger ID returned from add_message_listener
494
+ #
495
+ def delete_message_listener(lid)
496
+ @messageListeners.delete(lid)
497
+ end
498
+
499
+ #Cleanup methods
500
+
501
+ ##
502
+ # Releases the connection and resets the session. The Session instance
503
+ # is no longer usable after this method is called
504
+ #
505
+ def release
506
+ begin
507
+ @connection.on_connection_exception do
508
+ #Do nothing...we are shutting down
509
+ end
510
+ @connection.send(Jabber::Protocol::Presence.gen_unavailable(id))
511
+ @connection.send(Jabber::Protocol.gen_close_stream)
512
+ rescue
513
+ #ignore error
514
+ end
515
+ begin
516
+ @connection.close
517
+ rescue
518
+ #ignore error
519
+ end
520
+ end
521
+
522
+ ##
523
+ # Same as _release
524
+ #
525
+ def close
526
+ release
527
+ end
528
+
529
+ ##
530
+ # Requests the Roster for the (authenticated) account. This method blocks
531
+ # until a reply to the roster request is received.
532
+ #
533
+ def request_roster
534
+ if @authenticated
535
+ msg_id = id
536
+ @connection.send(Jabber::Protocol::Iq.gen_roster(self, msg_id)) do |element|
537
+ if element.attr_id == msg_id
538
+ element.consume_element
539
+ element.query.item.count.times do |i|
540
+ item = element.query.item[i]
541
+ @roster.add(item.attr_jid, item.attr_subscription, item.attr_name, item.group.element_data)
542
+ end
543
+ end
544
+ end
545
+ Thread.stop
546
+ register_roster_filter
547
+ end
548
+ end
549
+
550
+ ##
551
+ # Registers the roster filter with the Connection to forward IQ requests
552
+ # to the IQ listeners(they register by namespace)
553
+ #
554
+ def register_iq_filter()
555
+ @connection.add_filter("iqFilter") do |element|
556
+ if element.element_tag=="iq" then
557
+ element.consume_element
558
+ query=element.query
559
+ h=@iqHandlers[query.attr_xmlns]
560
+ h.call(Jabber::Protocol::Iq.from_element(self,element)) if h
561
+ end
562
+ end
563
+ end
564
+
565
+
566
+ ##
567
+ # Registers the roster filter with the Connection to forward roster changes to
568
+ # the roster listeners.
569
+ #
570
+ def register_roster_filter
571
+ @connection.add_filter("rosterFilter") do |element|
572
+ if element.element_tag=="iq" and element.query.attr_xmlns=="jabber:iq:roster" and element.attr_type=="set"
573
+ element.consume_element
574
+ item = element.query.item
575
+ if item.attr_subscription=="remove" then
576
+ @roster.remove(item.attr_jid)
577
+ else
578
+ @roster.add(item.attr_jid, item.attr_subscription, item.attr_name, item.group.element_data)
579
+ end
580
+ end
581
+ end
582
+ end
583
+
584
+ ##
585
+ # Registers a listener for roster events
586
+ #
587
+ # &block:: [Block] The listener block to process roster changes
588
+ # return:: [String] A roster ID to use when removing this listener
589
+ #
590
+ def add_roster_listener(&block)
591
+ roster.add_listener(&block)
592
+ end
593
+
594
+ ##
595
+ # Deletes the roster listener
596
+ #
597
+ # id:: [String] A roster ID received from the add_roster_listener method
598
+ #
599
+ def delete_roster_listener(id)
600
+ roster.delete_listener(id)
601
+ end
602
+
603
+ ##
604
+ # Notifies message listeners of the received message
605
+ #
606
+ # message:: [Jabber::Protocol::Message] The received message
607
+ #
608
+ def notify_message_listeners(message)
609
+ @messageListeners.each_value {|listener| listener.call(message)}
610
+ end
611
+
612
+ end
613
+
614
+ end
615
+