xmpp4r 0.3

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.
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
+