xmpp4r 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. data/COPYING +340 -0
  2. data/ChangeLog +28 -0
  3. data/LICENSE +59 -0
  4. data/README +20 -0
  5. data/Rakefile +103 -0
  6. data/UPDATING +40 -0
  7. data/data/doc/xmpp4r/examples/advanced/adventure/README +57 -0
  8. data/data/doc/xmpp4r/examples/advanced/adventure/adventure.rb +23 -0
  9. data/data/doc/xmpp4r/examples/advanced/adventure/adventuremuc.rb +136 -0
  10. data/data/doc/xmpp4r/examples/advanced/adventure/cube.xml +15 -0
  11. data/data/doc/xmpp4r/examples/advanced/adventure/tower.xml +69 -0
  12. data/data/doc/xmpp4r/examples/advanced/adventure/world.rb +425 -0
  13. data/data/doc/xmpp4r/examples/advanced/fileserve.conf +11 -0
  14. data/data/doc/xmpp4r/examples/advanced/fileserve.rb +344 -0
  15. data/data/doc/xmpp4r/examples/advanced/getonline.rb +56 -0
  16. data/data/doc/xmpp4r/examples/advanced/gtkmucclient.rb +315 -0
  17. data/data/doc/xmpp4r/examples/advanced/migrate.rb +89 -0
  18. data/data/doc/xmpp4r/examples/advanced/minimuc.rb +266 -0
  19. data/data/doc/xmpp4r/examples/advanced/recvfile.rb +83 -0
  20. data/data/doc/xmpp4r/examples/advanced/rosterdiscovery.rb +130 -0
  21. data/data/doc/xmpp4r/examples/advanced/sendfile.conf +10 -0
  22. data/data/doc/xmpp4r/examples/advanced/sendfile.rb +72 -0
  23. data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr.rb +51 -0
  24. data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_jabber.rb +43 -0
  25. data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_test.rb +10 -0
  26. data/data/doc/xmpp4r/examples/advanced/versionpoll.rb +90 -0
  27. data/data/doc/xmpp4r/examples/advanced/xmpping.rb +134 -0
  28. data/data/doc/xmpp4r/examples/advanced/xmppingrc.sample +9 -0
  29. data/data/doc/xmpp4r/examples/basic/change_password.rb +41 -0
  30. data/data/doc/xmpp4r/examples/basic/client.rb +68 -0
  31. data/data/doc/xmpp4r/examples/basic/component.rb +11 -0
  32. data/data/doc/xmpp4r/examples/basic/echo_nonthreaded.rb +32 -0
  33. data/data/doc/xmpp4r/examples/basic/echo_threaded.rb +32 -0
  34. data/data/doc/xmpp4r/examples/basic/jabbersend.rb +41 -0
  35. data/data/doc/xmpp4r/examples/basic/mass_sender.rb +67 -0
  36. data/data/doc/xmpp4r/examples/basic/mucinfo.rb +39 -0
  37. data/data/doc/xmpp4r/examples/basic/mucsimplebot.rb +83 -0
  38. data/data/doc/xmpp4r/examples/basic/register.rb +25 -0
  39. data/data/doc/xmpp4r/examples/basic/remove_registration.rb +18 -0
  40. data/data/doc/xmpp4r/examples/basic/roster.rb +42 -0
  41. data/data/doc/xmpp4r/examples/basic/rosterprint.rb +50 -0
  42. data/data/doc/xmpp4r/examples/basic/rosterrename.rb +34 -0
  43. data/data/doc/xmpp4r/examples/basic/rosterwatch.rb +172 -0
  44. data/data/doc/xmpp4r/examples/basic/send_vcard.rb +68 -0
  45. data/data/doc/xmpp4r/examples/basic/versionbot.rb +75 -0
  46. data/data/doc/xmpp4r/examples/buggy/jabber2jabber/jabber2jabber.rb +18 -0
  47. data/data/doc/xmpp4r/examples/buggy/miniedgarr_cgi.rb +192 -0
  48. data/data/doc/xmpp4r/examples/buggy/miniedgarr_watch.rb +82 -0
  49. data/lib/callbacks.rb +122 -0
  50. data/lib/xmpp4r/authenticationfailure.rb +13 -0
  51. data/lib/xmpp4r/bytestreams/helper/filetransfer.rb +315 -0
  52. data/lib/xmpp4r/bytestreams/helper/ibb/base.rb +256 -0
  53. data/lib/xmpp4r/bytestreams/helper/ibb/initiator.rb +30 -0
  54. data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +46 -0
  55. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb +151 -0
  56. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb +85 -0
  57. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb +178 -0
  58. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb +56 -0
  59. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +61 -0
  60. data/lib/xmpp4r/bytestreams/iq/bytestreams.rb +177 -0
  61. data/lib/xmpp4r/bytestreams/iq/si.rb +221 -0
  62. data/lib/xmpp4r/bytestreams.rb +11 -0
  63. data/lib/xmpp4r/client.rb +249 -0
  64. data/lib/xmpp4r/component.rb +103 -0
  65. data/lib/xmpp4r/connection.rb +166 -0
  66. data/lib/xmpp4r/dataforms/x/data.rb +248 -0
  67. data/lib/xmpp4r/dataforms.rb +1 -0
  68. data/lib/xmpp4r/debuglog.rb +34 -0
  69. data/lib/xmpp4r/delay/x/delay.rb +100 -0
  70. data/lib/xmpp4r/delay.rb +1 -0
  71. data/lib/xmpp4r/discovery/iq/discoinfo.rb +225 -0
  72. data/lib/xmpp4r/discovery/iq/discoitems.rb +162 -0
  73. data/lib/xmpp4r/discovery.rb +2 -0
  74. data/lib/xmpp4r/error.rb +230 -0
  75. data/lib/xmpp4r/errorexception.rb +32 -0
  76. data/lib/xmpp4r/feature_negotiation/iq/feature.rb +42 -0
  77. data/lib/xmpp4r/feature_negotiation.rb +1 -0
  78. data/lib/xmpp4r/idgenerator.rb +37 -0
  79. data/lib/xmpp4r/iq.rb +229 -0
  80. data/lib/xmpp4r/jid.rb +167 -0
  81. data/lib/xmpp4r/message.rb +171 -0
  82. data/lib/xmpp4r/muc/helper/mucbrowser.rb +107 -0
  83. data/lib/xmpp4r/muc/helper/mucclient.rb +382 -0
  84. data/lib/xmpp4r/muc/helper/simplemucclient.rb +222 -0
  85. data/lib/xmpp4r/muc/x/muc.rb +98 -0
  86. data/lib/xmpp4r/muc/x/mucuserinvite.rb +58 -0
  87. data/lib/xmpp4r/muc/x/mucuseritem.rb +148 -0
  88. data/lib/xmpp4r/muc.rb +6 -0
  89. data/lib/xmpp4r/presence.rb +255 -0
  90. data/lib/xmpp4r/query.rb +43 -0
  91. data/lib/xmpp4r/rexmladdons.rb +826 -0
  92. data/lib/xmpp4r/roster/helper/roster.rb +514 -0
  93. data/lib/xmpp4r/roster/iq/roster.rb +244 -0
  94. data/lib/xmpp4r/roster/x/roster.rb +155 -0
  95. data/lib/xmpp4r/roster.rb +4 -0
  96. data/lib/xmpp4r/sasl.rb +167 -0
  97. data/lib/xmpp4r/stream.rb +543 -0
  98. data/lib/xmpp4r/streamparser.rb +77 -0
  99. data/lib/xmpp4r/vcard/helper/vcard.rb +86 -0
  100. data/lib/xmpp4r/vcard/iq/vcard.rb +102 -0
  101. data/lib/xmpp4r/vcard.rb +3 -0
  102. data/lib/xmpp4r/version/helper/responder.rb +71 -0
  103. data/lib/xmpp4r/version/helper/simpleresponder.rb +44 -0
  104. data/lib/xmpp4r/version/iq/version.rb +118 -0
  105. data/lib/xmpp4r/version.rb +3 -0
  106. data/lib/xmpp4r/x.rb +43 -0
  107. data/lib/xmpp4r/xmlstanza.rb +174 -0
  108. data/lib/xmpp4r/xmpp4r.rb +16 -0
  109. data/lib/xmpp4r.rb +122 -0
  110. data/setup.rb +1360 -0
  111. data/test/bytestreams/tc_ibb.rb +186 -0
  112. data/test/bytestreams/tc_socks5bytestreams.rb +57 -0
  113. data/test/delay/tc_xdelay.rb +51 -0
  114. data/test/lib/clienttester.rb +110 -0
  115. data/test/muc/tc_muc_mucclient.rb +569 -0
  116. data/test/muc/tc_muc_simplemucclient.rb +72 -0
  117. data/test/roster/.tc_helper.rb.swp +0 -0
  118. data/test/roster/tc_helper.rb +389 -0
  119. data/test/roster/tc_iqqueryroster.rb +140 -0
  120. data/test/roster/tc_xroster.rb +70 -0
  121. data/test/tc_callbacks.rb +128 -0
  122. data/test/tc_class_names.rb +129 -0
  123. data/test/tc_client.rb +30 -0
  124. data/test/tc_error.rb +103 -0
  125. data/test/tc_idgenerator.rb +30 -0
  126. data/test/tc_iq.rb +109 -0
  127. data/test/tc_iqquery.rb +31 -0
  128. data/test/tc_jid.rb +202 -0
  129. data/test/tc_message.rb +114 -0
  130. data/test/tc_presence.rb +148 -0
  131. data/test/tc_stream.rb +182 -0
  132. data/test/tc_streamError.rb +87 -0
  133. data/test/tc_streamSend.rb +59 -0
  134. data/test/tc_streamThreaded.rb +168 -0
  135. data/test/tc_xmlstanza.rb +76 -0
  136. data/test/ts_xmpp4r.rb +34 -0
  137. data/test/vcard/tc_iqvcard.rb +52 -0
  138. data/test/version/tc_helper.rb +46 -0
  139. data/test/version/tc_iqqueryversion.rb +96 -0
  140. data/tools/doctoweb.bash +30 -0
  141. data/tools/gen_requires.bash +10 -0
  142. metadata +232 -0
@@ -0,0 +1,543 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
5
+ require 'callbacks'
6
+ require 'socket'
7
+ require 'thread'
8
+ Thread::abort_on_exception = true
9
+ require 'xmpp4r/streamparser'
10
+ require 'xmpp4r/presence'
11
+ require 'xmpp4r/message'
12
+ require 'xmpp4r/iq'
13
+ require 'xmpp4r/errorexception'
14
+ require 'xmpp4r/debuglog'
15
+ require 'xmpp4r/idgenerator'
16
+
17
+ module Jabber
18
+ ##
19
+ # The stream class manages a connection stream (a file descriptor using which
20
+ # XML messages are read and sent)
21
+ class Stream
22
+ DISCONNECTED = 1
23
+ CONNECTED = 2
24
+
25
+ # file descriptor used
26
+ attr_reader :fd
27
+
28
+ # connection status
29
+ attr_reader :status
30
+
31
+ ##
32
+ # Create a new stream
33
+ # (just initializes)
34
+ def initialize(threaded = true)
35
+ @fd = nil
36
+ @status = DISCONNECTED
37
+ @xmlcbs = CallbackList::new
38
+ @stanzacbs = CallbackList::new
39
+ @messagecbs = CallbackList::new
40
+ @iqcbs = CallbackList::new
41
+ @presencecbs = CallbackList::new
42
+ unless threaded
43
+ $stderr.puts "Non-threaded mode is currently broken, re-enabling threaded"
44
+ threaded = true
45
+ end
46
+ @threaded = threaded
47
+ @stanzaqueue = []
48
+ @stanzaqueue_lock = Mutex::new
49
+ @exception_block = nil
50
+ @threadblocks = []
51
+ # @pollCounter = 10
52
+ @waiting_thread = nil
53
+ @wakeup_thread = nil
54
+ @streamid = nil
55
+ @features_lock = Mutex.new
56
+ end
57
+
58
+ ##
59
+ # Start the XML parser on the fd
60
+ def start(fd)
61
+ @stream_mechanisms = []
62
+ @stream_features = {}
63
+
64
+ @fd = fd
65
+ @parser = StreamParser.new(@fd, self)
66
+ @parserThread = Thread.new do
67
+ begin
68
+ @parser.parse
69
+ rescue Exception => e
70
+ Jabber::debuglog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
71
+
72
+ if @exception_block
73
+ Thread.new { close; @exception_block.call(e, self, :start) }
74
+ else
75
+ puts "Exception caught in Parser thread!"
76
+ close
77
+ raise
78
+ end
79
+ end
80
+ end
81
+ # @pollThread = Thread.new do
82
+ # begin
83
+ # poll
84
+ # rescue
85
+ # puts "Exception caught in Poll thread, dumping backtrace and" +
86
+ # " exiting...\n" + $!.exception + "\n"
87
+ # puts $!.backtrace
88
+ # exit
89
+ # end
90
+ # end
91
+ @status = CONNECTED
92
+ end
93
+
94
+ def stop
95
+ @parserThread.kill
96
+ @parser = nil
97
+ end
98
+
99
+ ##
100
+ # Mounts a block to handle exceptions if they occur during the
101
+ # poll send. This will likely be the first indication that
102
+ # the socket dropped in a Jabber Session.
103
+ #
104
+ # The block has to take three arguments:
105
+ # * the Exception
106
+ # * the Jabber::Stream object (self)
107
+ # * a symbol where it happened, namely :start, :parser, :sending and :end
108
+ def on_exception(&block)
109
+ @exception_block = block
110
+ end
111
+
112
+ ##
113
+ # This method is called by the parser when a failure occurs
114
+ def parse_failure(e)
115
+ Jabber::debuglog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
116
+
117
+ # A new thread has to be created because close will cause the thread
118
+ # to commit suicide(???)
119
+ if @exception_block
120
+ # New thread, because close will kill the current thread
121
+ Thread.new {
122
+ close
123
+ @exception_block.call(e, self, :parser)
124
+ }
125
+ else
126
+ puts "Stream#parse_failure was called by XML parser. Dumping " +
127
+ "backtrace...\n" + e.exception + "\n"
128
+ puts e.backtrace
129
+ close
130
+ raise
131
+ end
132
+ end
133
+
134
+ ##
135
+ # This method is called by the parser upon receiving <tt></stream:stream></tt>
136
+ def parser_end
137
+ if @exception_block
138
+ Thread.new {
139
+ close
140
+ @exception_block.call(nil, self, :close)
141
+ }
142
+ else
143
+ close
144
+ end
145
+ end
146
+
147
+ ##
148
+ # Returns if this connection is connected to a Jabber service
149
+ # return:: [Boolean] Connection status
150
+ def is_connected?
151
+ return @status == CONNECTED
152
+ end
153
+
154
+ ##
155
+ # Returns if this connection is NOT connected to a Jabber service
156
+ #
157
+ # return:: [Boolean] Connection status
158
+ def is_disconnected?
159
+ return @status == DISCONNECTED
160
+ end
161
+
162
+ ##
163
+ # Processes a received REXML::Element and executes
164
+ # registered thread blocks and filters against it.
165
+ #
166
+ # If in threaded mode, a new thread will be spawned
167
+ # for the call to receive_nonthreaded.
168
+ # element:: [REXML::Element] The received element
169
+ def receive(element)
170
+ if @threaded
171
+ # Don't spawn a new thread here. An implicit feature
172
+ # of XMPP is constant order of stanzas.
173
+ receive_nonthreaded(element)
174
+ else
175
+ receive_nonthreaded(element)
176
+ end
177
+ end
178
+
179
+ def receive_nonthreaded(element)
180
+ Jabber::debuglog("RECEIVED:\n#{element.to_s}")
181
+ case element.prefix
182
+ when 'stream'
183
+ case element.name
184
+ when 'stream'
185
+ stanza = element
186
+ @streamid = element.attributes['id']
187
+ unless element.attributes['version'] # isn't XMPP compliant, so
188
+ Jabber::debuglog("FEATURES: server not XMPP compliant, will not wait for features")
189
+ @features_lock.unlock # don't wait for <stream:features/>
190
+ end
191
+ when 'features'
192
+ stanza = element
193
+ element.each { |e|
194
+ if e.name == 'mechanisms' and e.namespace == 'urn:ietf:params:xml:ns:xmpp-sasl'
195
+ e.each_element('mechanism') { |mech|
196
+ @stream_mechanisms.push(mech.text)
197
+ }
198
+ else
199
+ @stream_features[e.name] = e.namespace
200
+ end
201
+ }
202
+ Jabber::debuglog("FEATURES: received")
203
+ @features_lock.unlock
204
+ else
205
+ stanza = element
206
+ end
207
+ else
208
+ case element.name
209
+ when 'message'
210
+ stanza = Message::import(element)
211
+ when 'iq'
212
+ stanza = Iq::import(element)
213
+ when 'presence'
214
+ stanza = Presence::import(element)
215
+ else
216
+ stanza = element
217
+ end
218
+ end
219
+
220
+ # Iterate through blocked threads (= waiting for an answer)
221
+ #
222
+ # We're dup'ping the @threadblocks here, so that we won't end up in an
223
+ # endless loop if Stream#send is being nested. That means, the nested
224
+ # threadblock won't receive the stanza currently processed, but the next
225
+ # one.
226
+ threadblocks = @threadblocks.dup
227
+ threadblocks.each { |threadblock|
228
+ exception = nil
229
+ r = false
230
+ begin
231
+ r = threadblock.call(stanza)
232
+ rescue Exception => e
233
+ exception = e
234
+ end
235
+
236
+ if r == true
237
+ @threadblocks.delete(threadblock)
238
+ threadblock.wakeup
239
+ return
240
+ elsif exception
241
+ @threadblocks.delete(threadblock)
242
+ threadblock.raise(exception)
243
+ end
244
+ }
245
+
246
+ if @threaded
247
+ process_one(stanza)
248
+ else
249
+ # stanzaqueue will be read when the user call process
250
+ @stanzaqueue_lock.lock
251
+ @stanzaqueue.push(stanza)
252
+ @stanzaqueue_lock.unlock
253
+ @waiting_thread.wakeup if @waiting_thread
254
+ end
255
+ end
256
+ private :receive_nonthreaded
257
+
258
+ ##
259
+ # Process |element| until it is consumed. Returns element.consumed?
260
+ # element The element to process
261
+ def process_one(stanza)
262
+ Jabber::debuglog("PROCESSING:\n#{stanza.to_s}")
263
+ return true if @xmlcbs.process(stanza)
264
+ return true if @stanzacbs.process(stanza)
265
+ case stanza
266
+ when Message
267
+ return true if @messagecbs.process(stanza)
268
+ when Iq
269
+ return true if @iqcbs.process(stanza)
270
+ when Presence
271
+ return true if @presencecbs.process(stanza)
272
+ end
273
+ end
274
+ private :process_one
275
+
276
+ ##
277
+ # Process |max| XML stanzas and call listeners for all of them.
278
+ #
279
+ # max:: [Integer] the number of stanzas to process (nil means process
280
+ # all available)
281
+ def process(max = nil)
282
+ n = 0
283
+ @stanzaqueue_lock.lock
284
+ while @stanzaqueue.size > 0 and (max == nil or n < max)
285
+ e = @stanzaqueue.shift
286
+ @stanzaqueue_lock.unlock
287
+ process_one(e)
288
+ n += 1
289
+ @stanzaqueue_lock.lock
290
+ end
291
+ @stanzaqueue_lock.unlock
292
+ n
293
+ end
294
+
295
+ ##
296
+ # Process an XML stanza and call the listeners for it. If no stanza is
297
+ # currently available, wait for max |time| seconds before returning.
298
+ #
299
+ # time:: [Integer] time to wait in seconds. If nil, wait infinitely.
300
+ # all available)
301
+ def wait_and_process(time = nil)
302
+ if time == 0
303
+ return process(1)
304
+ end
305
+ @stanzaqueue_lock.lock
306
+ if @stanzaqueue.size > 0
307
+ e = @stanzaqueue.shift
308
+ @stanzaqueue_lock.unlock
309
+ process_one(e)
310
+ return 1
311
+ end
312
+
313
+ @waiting_thread = Thread.current
314
+ @wakeup_thread = Thread.new { sleep time ; @waiting_thread.wakeup if @waiting_thread }
315
+ @waiting_thread.stop
316
+ @wakeup_thread.kill if @wakeup_thread
317
+ @wakeup_thread = nil
318
+ @waiting_thread = nil
319
+
320
+ @stanzaqueue_lock.lock
321
+ if @stanzaqueue.size > 0
322
+ e = @stanzaqueue.shift
323
+ @stanzaqueue_lock.unlock
324
+ process_one(e)
325
+ return 1
326
+ end
327
+ return 0
328
+ end
329
+
330
+ ##
331
+ # This is used by Jabber::Stream internally to
332
+ # keep track of any blocks which were passed to
333
+ # Stream#send.
334
+ class ThreadBlock
335
+ def initialize(block)
336
+ @thread = Thread.current
337
+ @block = block
338
+ end
339
+ def call(*args)
340
+ @block.call(*args)
341
+ end
342
+ def wakeup
343
+ # TODO: Handle threadblock removal if !alive?
344
+ @thread.wakeup if @thread.alive?
345
+ end
346
+ def raise(exception)
347
+ @thread.raise(exception) if @thread.alive?
348
+ end
349
+ end
350
+
351
+ ##
352
+ # Sends XML data to the socket and (optionally) waits
353
+ # to process received data.
354
+ #
355
+ # xml:: [String] The xml data to send
356
+ # &block:: [Block] The optional block
357
+ def send(xml, &block)
358
+ Jabber::debuglog("SENDING:\n#{xml}")
359
+ @threadblocks.unshift(ThreadBlock.new(block)) if block
360
+ Thread.critical = true # we don't want to be interupted before we stop!
361
+ begin
362
+ @fd << xml.to_s
363
+ @fd.flush
364
+ rescue Exception => e
365
+ Jabber::debuglog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
366
+
367
+ if @exception_block
368
+ Thread.new { close!; @exception_block.call(e, self, :sending) }
369
+ else
370
+ puts "Exception caught while sending!"
371
+ close!
372
+ raise
373
+ end
374
+ end
375
+ Thread.critical = false
376
+ # The parser thread might be running this (think of a callback running send())
377
+ # If this is the case, we mustn't stop (or we would cause a deadlock)
378
+ Thread.stop if block and Thread.current != @parserThread
379
+ @pollCounter = 10
380
+ end
381
+
382
+ ##
383
+ # Send an XMMP stanza with an Jabber::XMLStanza#id. The id will be
384
+ # generated by Jabber::IdGenerator if not already set.
385
+ #
386
+ # The block will be called once: when receiving a stanza with the
387
+ # same Jabber::XMLStanza#id. It *must* return true to complete this!
388
+ #
389
+ # Be aware that if a stanza with <tt>type='error'</tt> is received
390
+ # the function does not yield but raises an ErrorException with
391
+ # the corresponding error element.
392
+ #
393
+ # Please read the note about nesting at Stream#send
394
+ # xml:: [XMLStanza]
395
+ def send_with_id(xml, &block)
396
+ if xml.id.nil?
397
+ xml.id = Jabber::IdGenerator.instance.generate_id
398
+ end
399
+
400
+ error = nil
401
+ send(xml) do |received|
402
+ if received.kind_of? XMLStanza and received.id == xml.id
403
+ if received.type == :error
404
+ error = (received.error ? received.error : Error.new)
405
+ true
406
+ else
407
+ yield(received)
408
+ end
409
+ else
410
+ false
411
+ end
412
+ end
413
+
414
+ unless error.nil?
415
+ raise ErrorException.new(error)
416
+ end
417
+ end
418
+
419
+ ##
420
+ # Starts a polling thread to send "keep alive" data to prevent
421
+ # the Jabber connection from closing for inactivity.
422
+ #
423
+ # Currently not working!
424
+ def poll
425
+ sleep 10
426
+ while true
427
+ sleep 2
428
+ # @pollCounter = @pollCounter - 1
429
+ # if @pollCounter < 0
430
+ # begin
431
+ # send(" \t ")
432
+ # rescue
433
+ # Thread.new {@exception_block.call if @exception_block}
434
+ # break
435
+ # end
436
+ # end
437
+ end
438
+ end
439
+
440
+ ##
441
+ # Adds a callback block to process received XML messages
442
+ #
443
+ # priority:: [Integer] The callback's priority, the higher, the sooner
444
+ # ref:: [String] The callback's reference
445
+ # &block:: [Block] The optional block
446
+ def add_xml_callback(priority = 0, ref = nil, &block)
447
+ @xmlcbs.add(priority, ref, block)
448
+ end
449
+
450
+ ##
451
+ # Delete an XML-messages callback
452
+ #
453
+ # ref:: [String] The reference of the callback to delete
454
+ def delete_xml_callback(ref)
455
+ @xmlcbs.delete(ref)
456
+ end
457
+
458
+ ##
459
+ # Adds a callback block to process received Messages
460
+ #
461
+ # priority:: [Integer] The callback's priority, the higher, the sooner
462
+ # ref:: [String] The callback's reference
463
+ # &block:: [Block] The optional block
464
+ def add_message_callback(priority = 0, ref = nil, &block)
465
+ @messagecbs.add(priority, ref, block)
466
+ end
467
+
468
+ ##
469
+ # Delete an Message callback
470
+ #
471
+ # ref:: [String] The reference of the callback to delete
472
+ def delete_message_callback(ref)
473
+ @messagecbs.delete(ref)
474
+ end
475
+
476
+ ##
477
+ # Adds a callback block to process received Stanzas
478
+ #
479
+ # priority:: [Integer] The callback's priority, the higher, the sooner
480
+ # ref:: [String] The callback's reference
481
+ # &block:: [Block] The optional block
482
+ def add_stanza_callback(priority = 0, ref = nil, &block)
483
+ @stanzacbs.add(priority, ref, block)
484
+ end
485
+
486
+ ##
487
+ # Delete a Stanza callback
488
+ #
489
+ # ref:: [String] The reference of the callback to delete
490
+ def delete_stanza_callback(ref)
491
+ @stanzacbs.delete(ref)
492
+ end
493
+
494
+ ##
495
+ # Adds a callback block to process received Presences
496
+ #
497
+ # priority:: [Integer] The callback's priority, the higher, the sooner
498
+ # ref:: [String] The callback's reference
499
+ # &block:: [Block] The optional block
500
+ def add_presence_callback(priority = 0, ref = nil, &block)
501
+ @presencecbs.add(priority, ref, block)
502
+ end
503
+
504
+ ##
505
+ # Delete a Presence callback
506
+ #
507
+ # ref:: [String] The reference of the callback to delete
508
+ def delete_presence_callback(ref)
509
+ @presencecbs.delete(ref)
510
+ end
511
+
512
+ ##
513
+ # Adds a callback block to process received Iqs
514
+ #
515
+ # priority:: [Integer] The callback's priority, the higher, the sooner
516
+ # ref:: [String] The callback's reference
517
+ # &block:: [Block] The optional block
518
+ def add_iq_callback(priority = 0, ref = nil, &block)
519
+ @iqcbs.add(priority, ref, block)
520
+ end
521
+
522
+ ##
523
+ # Delete an Iq callback
524
+ #
525
+ # ref:: [String] The reference of the callback to delete
526
+ #
527
+ def delete_iq_callback(ref)
528
+ @iqcbs.delete(ref)
529
+ end
530
+ ##
531
+ # Closes the connection to the Jabber service
532
+ def close
533
+ close!
534
+ end
535
+
536
+ def close!
537
+ @parserThread.kill if @parserThread
538
+ # @pollThread.kill
539
+ @fd.close if @fd and !@fd.closed?
540
+ @status = DISCONNECTED
541
+ end
542
+ end
543
+ end
@@ -0,0 +1,77 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
5
+ require 'rexml/parsers/sax2parser'
6
+ require 'rexml/source'
7
+ require 'xmpp4r/rexmladdons'
8
+
9
+ module Jabber
10
+
11
+ ##
12
+ # The StreamParser uses REXML to parse the incoming XML stream
13
+ # of the Jabber protocol and fires XMLStanzas at the Connection
14
+ # instance.
15
+ #
16
+ class StreamParser
17
+ # status if the parser is started
18
+ attr_reader :started
19
+
20
+ ##
21
+ # Constructs a parser for the supplied stream (socket input)
22
+ #
23
+ # stream:: [IO] Socket input stream
24
+ # listener:: [Object.receive(XMLStanza)] The listener (usually a Jabber::Protocol::Connection instance)
25
+ #
26
+ def initialize(stream, listener)
27
+ @stream = stream
28
+ @listener = listener
29
+ @current = nil
30
+ end
31
+
32
+ ##
33
+ # Begins parsing the XML stream and does not return until
34
+ # the stream closes.
35
+ #
36
+ def parse
37
+ @started = false
38
+ begin
39
+ parser = REXML::Parsers::SAX2Parser.new @stream
40
+
41
+ parser.listen( :start_element ) do |uri, localname, qname, attributes|
42
+ e = REXML::Element::new(qname)
43
+ e.add_attributes attributes
44
+ @current = @current.nil? ? e : @current.add_element(e)
45
+
46
+ if @current.name == 'stream' and !@started
47
+ @started = true
48
+ @listener.receive(@current)
49
+ @current = nil
50
+ end
51
+ end
52
+
53
+ parser.listen( :end_element ) do |uri, localname, qname|
54
+ if qname == 'stream:stream' and @current.nil?
55
+ @started = false
56
+ @listener.parser_end
57
+ else
58
+ @listener.receive(@current) unless @current.parent
59
+ @current = @current.parent
60
+ end
61
+ end
62
+
63
+ parser.listen( :characters ) do | text |
64
+ @current.text = @current.text.to_s + text if @current
65
+ end
66
+
67
+ parser.listen( :cdata ) do | text |
68
+ raise "Not implemented !"
69
+ end
70
+
71
+ parser.parse
72
+ rescue REXML::ParseException => e
73
+ @listener.parse_failure(e)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,86 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
5
+ require 'xmpp4r/iq'
6
+ require 'xmpp4r/errorexception'
7
+
8
+ module Jabber
9
+ module Vcard
10
+ ##
11
+ # The Vcard helper retrieves vCards
12
+ class Helper
13
+ ##
14
+ # Initialize a new Vcard helper
15
+ def initialize(stream)
16
+ @stream = stream
17
+ end
18
+
19
+ ##
20
+ # Retrieve vCard of an entity
21
+ #
22
+ # Raises exception upon retrieval error, please catch that!
23
+ # (The exception is ErrorException and is raisen by
24
+ # Stream#send_with_id.
25
+ #
26
+ # Usage of Threads is suggested here as vCards can be very
27
+ # big (see <tt>/iq/vCard/PHOTO/BINVAL</tt>).
28
+ #
29
+ # jid:: [Jabber::JID] or nil (should be stripped, nil for the client's own vCard)
30
+ # result:: [Jabber::IqVcard] or nil (nil results may be handled as empty vCards)
31
+ def get(jid=nil)
32
+ res = nil
33
+ request = Iq.new(:get, jid)
34
+ request.from = @stream.jid # Enable components to use this
35
+ request.add(IqVcard.new)
36
+ @stream.send_with_id(request) { |answer|
37
+ # No check for sender or queryns needed (see send_with_id)
38
+ if answer.type == :result
39
+ res = answer.vcard
40
+ true
41
+ else
42
+ false
43
+ end
44
+ }
45
+ res
46
+ end
47
+
48
+ ##
49
+ # Set your own vCard (Clients only)
50
+ #
51
+ # Raises exception when setting fails
52
+ #
53
+ # Usage of Threads suggested here, too. The function
54
+ # waits for approval from the server.
55
+ #
56
+ # iqvcard:: [Jabber::IqVcard]
57
+ def set(iqvcard)
58
+ iq = Iq.new(:set)
59
+ iq.add(iqvcard)
60
+
61
+ @stream.send_with_id(iq) { |answer|
62
+ if answer.type == :result
63
+ true
64
+ else
65
+ false
66
+ end
67
+ }
68
+ end
69
+
70
+ ##
71
+ # Quickly initialize a Vcard helper and get
72
+ # a vCard. See Vcard#get
73
+ def Vcard.get(stream, jid=nil)
74
+ Vcard.new(stream).get(jid)
75
+ end
76
+
77
+ ##
78
+ # Quickly initialize a Vcard helper and set
79
+ # your vCard. See Vcard#set
80
+ def Vcard.set(stream, iqvcard)
81
+ Vcard.new(stream).set(iqvcard)
82
+ end
83
+ end
84
+ end
85
+ end
86
+