mojodna-xmpp4r 0.4.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (208) hide show
  1. data/CHANGELOG +83 -0
  2. data/COPYING +340 -0
  3. data/LICENSE +59 -0
  4. data/README.rdoc +133 -0
  5. data/README_ruby19.txt +43 -0
  6. data/Rakefile +252 -0
  7. data/data/doc/xmpp4r/examples/advanced/adventure/README +56 -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 +424 -0
  13. data/data/doc/xmpp4r/examples/advanced/fileserve.conf +11 -0
  14. data/data/doc/xmpp4r/examples/advanced/fileserve.rb +346 -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 +88 -0
  18. data/data/doc/xmpp4r/examples/advanced/minimuc.rb +266 -0
  19. data/data/doc/xmpp4r/examples/advanced/pep-aggregator/index.xsl +235 -0
  20. data/data/doc/xmpp4r/examples/advanced/pep-aggregator/pep-aggregator.rb +147 -0
  21. data/data/doc/xmpp4r/examples/advanced/recvfile.rb +84 -0
  22. data/data/doc/xmpp4r/examples/advanced/rosterdiscovery.rb +129 -0
  23. data/data/doc/xmpp4r/examples/advanced/sendfile.conf +10 -0
  24. data/data/doc/xmpp4r/examples/advanced/sendfile.rb +72 -0
  25. data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr.rb +51 -0
  26. data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_jabber.rb +43 -0
  27. data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_test.rb +10 -0
  28. data/data/doc/xmpp4r/examples/advanced/versionpoll.rb +109 -0
  29. data/data/doc/xmpp4r/examples/advanced/xmpping.rb +146 -0
  30. data/data/doc/xmpp4r/examples/advanced/xmppingrc.sample +14 -0
  31. data/data/doc/xmpp4r/examples/basic/change_password.rb +41 -0
  32. data/data/doc/xmpp4r/examples/basic/client.rb +70 -0
  33. data/data/doc/xmpp4r/examples/basic/component.rb +11 -0
  34. data/data/doc/xmpp4r/examples/basic/echo.rb +37 -0
  35. data/data/doc/xmpp4r/examples/basic/jabbersend.rb +41 -0
  36. data/data/doc/xmpp4r/examples/basic/mass_sender.rb +68 -0
  37. data/data/doc/xmpp4r/examples/basic/muc_owner_config.rb +12 -0
  38. data/data/doc/xmpp4r/examples/basic/mucinfo.rb +41 -0
  39. data/data/doc/xmpp4r/examples/basic/mucsimplebot.rb +82 -0
  40. data/data/doc/xmpp4r/examples/basic/register.rb +42 -0
  41. data/data/doc/xmpp4r/examples/basic/remove_registration.rb +18 -0
  42. data/data/doc/xmpp4r/examples/basic/roster.rb +44 -0
  43. data/data/doc/xmpp4r/examples/basic/rosterprint.rb +50 -0
  44. data/data/doc/xmpp4r/examples/basic/rosterrename.rb +34 -0
  45. data/data/doc/xmpp4r/examples/basic/rosterwatch.rb +171 -0
  46. data/data/doc/xmpp4r/examples/basic/send_vcard.rb +67 -0
  47. data/data/doc/xmpp4r/examples/basic/tune_client.rb +56 -0
  48. data/data/doc/xmpp4r/examples/basic/tune_server.rb +58 -0
  49. data/data/doc/xmpp4r/examples/basic/versionbot.rb +75 -0
  50. data/lib/xmpp4r/base64.rb +32 -0
  51. data/lib/xmpp4r/bytestreams/helper/filetransfer.rb +319 -0
  52. data/lib/xmpp4r/bytestreams/helper/ibb/base.rb +257 -0
  53. data/lib/xmpp4r/bytestreams/helper/ibb/initiator.rb +31 -0
  54. data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +47 -0
  55. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb +152 -0
  56. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb +86 -0
  57. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb +198 -0
  58. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb +65 -0
  59. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +73 -0
  60. data/lib/xmpp4r/bytestreams/iq/bytestreams.rb +170 -0
  61. data/lib/xmpp4r/bytestreams/iq/si.rb +206 -0
  62. data/lib/xmpp4r/bytestreams.rb +15 -0
  63. data/lib/xmpp4r/callbacks.rb +133 -0
  64. data/lib/xmpp4r/caps/c.rb +67 -0
  65. data/lib/xmpp4r/caps/helper/generator.rb +160 -0
  66. data/lib/xmpp4r/caps/helper/helper.rb +87 -0
  67. data/lib/xmpp4r/caps.rb +1 -0
  68. data/lib/xmpp4r/client.rb +344 -0
  69. data/lib/xmpp4r/command/helper/responder.rb +53 -0
  70. data/lib/xmpp4r/command/iq/command.rb +154 -0
  71. data/lib/xmpp4r/component.rb +103 -0
  72. data/lib/xmpp4r/connection.rb +219 -0
  73. data/lib/xmpp4r/dataforms/x/data.rb +297 -0
  74. data/lib/xmpp4r/dataforms.rb +5 -0
  75. data/lib/xmpp4r/debuglog.rb +42 -0
  76. data/lib/xmpp4r/delay/x/delay.rb +99 -0
  77. data/lib/xmpp4r/delay.rb +5 -0
  78. data/lib/xmpp4r/discovery/helper/helper.rb +58 -0
  79. data/lib/xmpp4r/discovery/helper/responder.rb +165 -0
  80. data/lib/xmpp4r/discovery/iq/discoinfo.rb +211 -0
  81. data/lib/xmpp4r/discovery/iq/discoitems.rb +147 -0
  82. data/lib/xmpp4r/discovery.rb +8 -0
  83. data/lib/xmpp4r/errors.rb +283 -0
  84. data/lib/xmpp4r/feature_negotiation/iq/feature.rb +28 -0
  85. data/lib/xmpp4r/feature_negotiation.rb +5 -0
  86. data/lib/xmpp4r/framework/base.rb +55 -0
  87. data/lib/xmpp4r/framework/bot.rb +148 -0
  88. data/lib/xmpp4r/httpbinding/client.rb +275 -0
  89. data/lib/xmpp4r/httpbinding.rb +5 -0
  90. data/lib/xmpp4r/idgenerator.rb +37 -0
  91. data/lib/xmpp4r/iq.rb +221 -0
  92. data/lib/xmpp4r/jid.rb +167 -0
  93. data/lib/xmpp4r/last/helper/helper.rb +37 -0
  94. data/lib/xmpp4r/last/iq/last.rb +67 -0
  95. data/lib/xmpp4r/last.rb +2 -0
  96. data/lib/xmpp4r/location/helper/helper.rb +56 -0
  97. data/lib/xmpp4r/location/location.rb +179 -0
  98. data/lib/xmpp4r/location.rb +2 -0
  99. data/lib/xmpp4r/message.rb +181 -0
  100. data/lib/xmpp4r/muc/helper/mucbrowser.rb +92 -0
  101. data/lib/xmpp4r/muc/helper/mucclient.rb +462 -0
  102. data/lib/xmpp4r/muc/helper/simplemucclient.rb +332 -0
  103. data/lib/xmpp4r/muc/iq/mucadmin.rb +23 -0
  104. data/lib/xmpp4r/muc/iq/mucadminitem.rb +20 -0
  105. data/lib/xmpp4r/muc/iq/mucowner.rb +15 -0
  106. data/lib/xmpp4r/muc/item.rb +143 -0
  107. data/lib/xmpp4r/muc/x/muc.rb +70 -0
  108. data/lib/xmpp4r/muc/x/mucuserinvite.rb +60 -0
  109. data/lib/xmpp4r/muc/x/mucuseritem.rb +36 -0
  110. data/lib/xmpp4r/muc.rb +14 -0
  111. data/lib/xmpp4r/presence.rb +232 -0
  112. data/lib/xmpp4r/pubsub/children/configuration.rb +86 -0
  113. data/lib/xmpp4r/pubsub/children/event.rb +49 -0
  114. data/lib/xmpp4r/pubsub/children/item.rb +35 -0
  115. data/lib/xmpp4r/pubsub/children/items.rb +53 -0
  116. data/lib/xmpp4r/pubsub/children/node_config.rb +48 -0
  117. data/lib/xmpp4r/pubsub/children/publish.rb +38 -0
  118. data/lib/xmpp4r/pubsub/children/retract.rb +41 -0
  119. data/lib/xmpp4r/pubsub/children/subscription.rb +62 -0
  120. data/lib/xmpp4r/pubsub/children/subscription_config.rb +67 -0
  121. data/lib/xmpp4r/pubsub/children/unsubscribe.rb +48 -0
  122. data/lib/xmpp4r/pubsub/helper/nodebrowser.rb +130 -0
  123. data/lib/xmpp4r/pubsub/helper/nodehelper.rb +156 -0
  124. data/lib/xmpp4r/pubsub/helper/oauth_service_helper.rb +107 -0
  125. data/lib/xmpp4r/pubsub/helper/servicehelper.rb +456 -0
  126. data/lib/xmpp4r/pubsub/iq/pubsub.rb +19 -0
  127. data/lib/xmpp4r/pubsub.rb +8 -0
  128. data/lib/xmpp4r/query.rb +15 -0
  129. data/lib/xmpp4r/rexmladdons.rb +157 -0
  130. data/lib/xmpp4r/roster/helper/roster.rb +519 -0
  131. data/lib/xmpp4r/roster/iq/roster.rb +215 -0
  132. data/lib/xmpp4r/roster/x/roster.rb +138 -0
  133. data/lib/xmpp4r/roster.rb +7 -0
  134. data/lib/xmpp4r/rpc/helper/client.rb +123 -0
  135. data/lib/xmpp4r/rpc/helper/server.rb +74 -0
  136. data/lib/xmpp4r/rpc/helper/xmlrpcaddons.rb +67 -0
  137. data/lib/xmpp4r/rpc/iq/rpc.rb +23 -0
  138. data/lib/xmpp4r/rpc.rb +2 -0
  139. data/lib/xmpp4r/sasl.rb +243 -0
  140. data/lib/xmpp4r/semaphore.rb +38 -0
  141. data/lib/xmpp4r/stream.rb +531 -0
  142. data/lib/xmpp4r/streamparser.rb +81 -0
  143. data/lib/xmpp4r/tune/helper/helper.rb +58 -0
  144. data/lib/xmpp4r/tune/tune.rb +113 -0
  145. data/lib/xmpp4r/tune.rb +2 -0
  146. data/lib/xmpp4r/vcard/helper/vcard.rb +84 -0
  147. data/lib/xmpp4r/vcard/iq/vcard.rb +109 -0
  148. data/lib/xmpp4r/vcard.rb +6 -0
  149. data/lib/xmpp4r/version/helper/responder.rb +72 -0
  150. data/lib/xmpp4r/version/helper/simpleresponder.rb +44 -0
  151. data/lib/xmpp4r/version/iq/version.rb +105 -0
  152. data/lib/xmpp4r/version.rb +7 -0
  153. data/lib/xmpp4r/x.rb +37 -0
  154. data/lib/xmpp4r/xhtml/html.rb +115 -0
  155. data/lib/xmpp4r/xhtml.rb +1 -0
  156. data/lib/xmpp4r/xmpp4r.rb +18 -0
  157. data/lib/xmpp4r/xmppelement.rb +168 -0
  158. data/lib/xmpp4r/xmppstanza.rb +162 -0
  159. data/lib/xmpp4r.rb +116 -0
  160. data/setup.rb +1586 -0
  161. data/test/bytestreams/tc_ibb.rb +186 -0
  162. data/test/bytestreams/tc_socks5bytestreams.rb +113 -0
  163. data/test/caps/tc_helper.rb +156 -0
  164. data/test/dataforms/tc_data.rb +81 -0
  165. data/test/delay/tc_xdelay.rb +51 -0
  166. data/test/discovery/tc_responder.rb +91 -0
  167. data/test/lib/assert_equal_xml.rb +14 -0
  168. data/test/lib/clienttester.rb +120 -0
  169. data/test/muc/tc_muc_mucclient.rb +830 -0
  170. data/test/muc/tc_muc_simplemucclient.rb +114 -0
  171. data/test/muc/tc_mucowner.rb +50 -0
  172. data/test/pubsub/tc_helper.rb +722 -0
  173. data/test/pubsub/tc_nodeconfig.rb +54 -0
  174. data/test/pubsub/tc_subscriptionconfig.rb +41 -0
  175. data/test/roster/tc_helper.rb +514 -0
  176. data/test/roster/tc_iqqueryroster.rb +173 -0
  177. data/test/roster/tc_xroster.rb +73 -0
  178. data/test/rpc/tc_helper.rb +96 -0
  179. data/test/tc_callbacks.rb +129 -0
  180. data/test/tc_class_names.rb +146 -0
  181. data/test/tc_client.rb +30 -0
  182. data/test/tc_errors.rb +146 -0
  183. data/test/tc_idgenerator.rb +30 -0
  184. data/test/tc_iq.rb +113 -0
  185. data/test/tc_iqquery.rb +31 -0
  186. data/test/tc_jid.rb +204 -0
  187. data/test/tc_message.rb +132 -0
  188. data/test/tc_presence.rb +150 -0
  189. data/test/tc_rexml.rb +139 -0
  190. data/test/tc_stream.rb +229 -0
  191. data/test/tc_streamComponent.rb +95 -0
  192. data/test/tc_streamError.rb +131 -0
  193. data/test/tc_streamSend.rb +59 -0
  194. data/test/tc_streamparser.rb +120 -0
  195. data/test/tc_xmppstanza.rb +135 -0
  196. data/test/ts_xmpp4r.rb +53 -0
  197. data/test/tune/tc_helper_recv.rb +84 -0
  198. data/test/tune/tc_helper_send.rb +74 -0
  199. data/test/tune/tc_tune.rb +79 -0
  200. data/test/vcard/tc_helper.rb +49 -0
  201. data/test/vcard/tc_iqvcard.rb +62 -0
  202. data/test/version/tc_helper.rb +60 -0
  203. data/test/version/tc_iqqueryversion.rb +97 -0
  204. data/test/xhtml/tc_html.rb +41 -0
  205. data/tools/gen_requires.bash +31 -0
  206. data/tools/xmpp4r-gemspec-test.rb +11 -0
  207. data/xmpp4r.gemspec +304 -0
  208. metadata +346 -0
@@ -0,0 +1,531 @@
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/callbacks'
6
+ require 'socket'
7
+ require 'thread'
8
+ require 'xmpp4r/semaphore'
9
+ require 'xmpp4r/streamparser'
10
+ require 'xmpp4r/presence'
11
+ require 'xmpp4r/message'
12
+ require 'xmpp4r/iq'
13
+ require 'xmpp4r/debuglog'
14
+ require 'xmpp4r/idgenerator'
15
+
16
+ module Jabber
17
+ ##
18
+ # The stream class manages a connection stream (a file descriptor using which
19
+ # XML messages are read and sent)
20
+ #
21
+ # You may register callbacks for the three Jabber stanzas
22
+ # (message, presence and iq) and use the send and send_with_id
23
+ # methods.
24
+ #
25
+ # To ensure the order of received stanzas, callback blocks are
26
+ # launched in the parser thread. If further blocking operations
27
+ # are intended in those callbacks, run your own thread there.
28
+ class Stream
29
+ DISCONNECTED = 1
30
+ CONNECTED = 2
31
+
32
+ # file descriptor used
33
+ attr_reader :fd
34
+
35
+ # connection status
36
+ attr_reader :status
37
+
38
+ ##
39
+ # Initialize a new stream
40
+ def initialize
41
+ @fd = nil
42
+ @status = DISCONNECTED
43
+ @xmlcbs = CallbackList.new
44
+ @stanzacbs = CallbackList.new
45
+ @messagecbs = CallbackList.new
46
+ @iqcbs = CallbackList.new
47
+ @presencecbs = CallbackList.new
48
+ @send_lock = Mutex.new
49
+ @last_send = Time.now
50
+ @exception_block = nil
51
+ @threadblocks = []
52
+ @wakeup_thread = nil
53
+ @streamid = nil
54
+ @streamns = 'jabber:client'
55
+ @features_sem = Semaphore.new
56
+ @parser_thread = nil
57
+ end
58
+
59
+ ##
60
+ # Start the XML parser on the fd
61
+ def start(fd)
62
+ @stream_mechanisms = []
63
+ @stream_features = {}
64
+
65
+ @fd = fd
66
+ @parser = StreamParser.new(@fd, self)
67
+ @parser_thread = Thread.new do
68
+ Thread.current.abort_on_exception = true
69
+ begin
70
+ @parser.parse
71
+ Jabber::debuglog("DISCONNECTED\n")
72
+
73
+ if @exception_block
74
+ Thread.new { close!; @exception_block.call(nil, self, :disconnected) }
75
+ else
76
+ close!
77
+ end
78
+ rescue Exception => e
79
+ Jabber::debuglog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
80
+
81
+ if @exception_block
82
+ Thread.new do
83
+ Thread.current.abort_on_exception = true
84
+ close
85
+ @exception_block.call(e, self, :start)
86
+ end
87
+ else
88
+ Jabber::debuglog "Exception caught in Parser thread! (#{e.class})\n#{e.backtrace.join("\n")}"
89
+ close!
90
+ raise
91
+ end
92
+ end
93
+ end
94
+
95
+ @status = CONNECTED
96
+ end
97
+
98
+ def stop
99
+ @parser_thread.kill
100
+ @parser = nil
101
+ end
102
+
103
+ ##
104
+ # Mounts a block to handle exceptions if they occur during the
105
+ # poll send. This will likely be the first indication that
106
+ # the socket dropped in a Jabber Session.
107
+ #
108
+ # The block has to take three arguments:
109
+ # * the Exception
110
+ # * the Jabber::Stream object (self)
111
+ # * a symbol where it happened, namely :start, :parser, :sending and :end
112
+ def on_exception(&block)
113
+ @exception_block = block
114
+ end
115
+
116
+ ##
117
+ # This method is called by the parser when a failure occurs
118
+ def parse_failure(e)
119
+ Jabber::debuglog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
120
+
121
+ # A new thread has to be created because close will cause the thread
122
+ # to commit suicide(???)
123
+ if @exception_block
124
+ # New thread, because close will kill the current thread
125
+ Thread.new do
126
+ Thread.current.abort_on_exception = true
127
+ close
128
+ @exception_block.call(e, self, :parser)
129
+ end
130
+ else
131
+ Jabber::debuglog "Stream#parse_failure was called by XML parser. Dumping " +
132
+ "backtrace...\n" + e.exception + "\n#{e.backtrace.join("\n")}"
133
+ close
134
+ raise
135
+ end
136
+ end
137
+
138
+ ##
139
+ # This method is called by the parser upon receiving <tt></stream:stream></tt>
140
+ def parser_end
141
+ if @exception_block
142
+ Thread.new do
143
+ Thread.current.abort_on_exception = true
144
+ close
145
+ @exception_block.call(nil, self, :close)
146
+ end
147
+ else
148
+ close
149
+ end
150
+ end
151
+
152
+ ##
153
+ # Returns if this connection is connected to a Jabber service
154
+ # return:: [Boolean] Connection status
155
+ def is_connected?
156
+ return @status == CONNECTED
157
+ end
158
+
159
+ ##
160
+ # Returns if this connection is NOT connected to a Jabber service
161
+ #
162
+ # return:: [Boolean] Connection status
163
+ def is_disconnected?
164
+ return @status == DISCONNECTED
165
+ end
166
+
167
+ ##
168
+ # Processes a received REXML::Element and executes
169
+ # registered thread blocks and filters against it.
170
+ #
171
+ # element:: [REXML::Element] The received element
172
+ def receive(element)
173
+ Jabber::debuglog("RECEIVED:\n#{element.to_s}")
174
+
175
+ if element.namespace('').to_s == '' # REXML namespaces are always strings
176
+ element.add_namespace(@streamns)
177
+ end
178
+
179
+ case element.prefix
180
+ when 'stream'
181
+ case element.name
182
+ when 'stream'
183
+ stanza = element
184
+ @streamid = element.attributes['id']
185
+ @streamns = element.namespace('') if element.namespace('')
186
+
187
+ # Hack: component streams are basically client streams.
188
+ # Someday we may want to create special stanza classes
189
+ # for components/s2s deriving from normal stanzas but
190
+ # posessing these namespaces
191
+ @streamns = 'jabber:client' if @streamns == 'jabber:component:accept'
192
+
193
+ unless element.attributes['version'] # isn't XMPP compliant, so
194
+ Jabber::debuglog("FEATURES: server not XMPP compliant, will not wait for features")
195
+ @features_sem.run # don't wait for <stream:features/>
196
+ end
197
+ when 'features'
198
+ stanza = element
199
+ element.each { |e|
200
+ if e.name == 'mechanisms' and e.namespace == 'urn:ietf:params:xml:ns:xmpp-sasl'
201
+ e.each_element('mechanism') { |mech|
202
+ @stream_mechanisms.push(mech.text)
203
+ }
204
+ else
205
+ @stream_features[e.name] = e.namespace
206
+ end
207
+ }
208
+ Jabber::debuglog("FEATURES: received")
209
+ @features_sem.run
210
+ else
211
+ stanza = element
212
+ end
213
+ else
214
+ # Any stanza, classes are registered by XMPPElement::name_xmlns
215
+ begin
216
+ stanza = XMPPStanza::import(element)
217
+ rescue NoNameXmlnsRegistered
218
+ stanza = element
219
+ end
220
+ end
221
+
222
+ return true if @xmlcbs.process(stanza)
223
+
224
+ # Iterate through blocked threads (= waiting for an answer)
225
+ #
226
+ # We're dup'ping the @threadblocks here, so that we won't end up in an
227
+ # endless loop if Stream#send is being nested. That means, the nested
228
+ # threadblock won't receive the stanza currently processed, but the next
229
+ # one.
230
+ threadblocks = @threadblocks.dup
231
+ threadblocks.each { |threadblock|
232
+ exception = nil
233
+ r = false
234
+ begin
235
+ r = threadblock.call(stanza)
236
+ rescue Exception => e
237
+ exception = e
238
+ end
239
+
240
+ if r == true
241
+ @threadblocks.delete(threadblock)
242
+ threadblock.wakeup
243
+ return
244
+ elsif exception
245
+ @threadblocks.delete(threadblock)
246
+ threadblock.raise(exception)
247
+ end
248
+ }
249
+
250
+ Jabber::debuglog("PROCESSING:\n#{stanza.to_s} (#{stanza.class})")
251
+ return true if @stanzacbs.process(stanza)
252
+ case stanza
253
+ when Message
254
+ return true if @messagecbs.process(stanza)
255
+ when Iq
256
+ return true if @iqcbs.process(stanza)
257
+ when Presence
258
+ return true if @presencecbs.process(stanza)
259
+ end
260
+ end
261
+
262
+ ##
263
+ # Get the list of iq callbacks.
264
+ def iq_callbacks
265
+ @iqcbs
266
+ end
267
+
268
+ ##
269
+ # Get the list of message callbacks.
270
+ def message_callbacks
271
+ @messagecbs
272
+ end
273
+
274
+ ##
275
+ # Get the list of presence callbacks.
276
+ def presence_callbacks
277
+ @presencecbs
278
+ end
279
+
280
+ ##
281
+ # Get the list of stanza callbacks.
282
+ def stanza_callbacks
283
+ @stanzacbs
284
+ end
285
+
286
+ ##
287
+ # Get the list of xml callbacks.
288
+ def xml_callbacks
289
+ @xmlcbs
290
+ end
291
+
292
+ ##
293
+ # This is used by Jabber::Stream internally to
294
+ # keep track of any blocks which were passed to
295
+ # Stream#send.
296
+ class ThreadBlock
297
+ def initialize(block)
298
+ @block = block
299
+ @waiter = Semaphore.new
300
+ @exception = nil
301
+ end
302
+ def call(*args)
303
+ @block.call(*args)
304
+ end
305
+ def wait
306
+ @waiter.wait
307
+ raise @exception if @exception
308
+ end
309
+ def wakeup
310
+ # TODO: Handle threadblock removal if !alive?
311
+ @waiter.run
312
+ end
313
+ def raise(exception)
314
+ @exception = exception
315
+ @waiter.run
316
+ end
317
+ end
318
+
319
+ def send_data(data)
320
+ @send_lock.synchronize do
321
+ @last_send = Time.now
322
+ @fd << data
323
+ @fd.flush
324
+ end
325
+ end
326
+
327
+ ##
328
+ # Sends XML data to the socket and (optionally) waits
329
+ # to process received data.
330
+ #
331
+ # Do not invoke this in a callback but in a seperate thread
332
+ # because we may not suspend the parser-thread (in whose
333
+ # context callbacks are executed).
334
+ #
335
+ # xml:: [String] The xml data to send
336
+ # &block:: [Block] The optional block
337
+ def send(xml, &block)
338
+ Jabber::debuglog("SENDING:\n#{xml}")
339
+ @threadblocks.unshift(threadblock = ThreadBlock.new(block)) if block
340
+ begin
341
+ # Temporarily remove stanza's namespace to
342
+ # reduce bandwidth consumption
343
+ if xml.kind_of? XMPPStanza and xml.namespace == 'jabber:client' and
344
+ xml.prefix != 'stream' and xml.name != 'stream'
345
+ xml.delete_namespace
346
+ send_data(xml.to_s)
347
+ xml.add_namespace(@streamns)
348
+ else
349
+ send_data(xml.to_s)
350
+ end
351
+ rescue Exception => e
352
+ Jabber::debuglog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
353
+
354
+ if @exception_block
355
+ Thread.new do
356
+ Thread.current.abort_on_exception = true
357
+ close!
358
+ @exception_block.call(e, self, :sending)
359
+ end
360
+ else
361
+ Jabber::debuglog "Exception caught while sending! (#{e.class})\n#{e.backtrace.join("\n")}"
362
+ close!
363
+ raise
364
+ end
365
+ end
366
+ # The parser thread might be running this (think of a callback running send())
367
+ # If this is the case, we mustn't stop (or we would cause a deadlock)
368
+ if block and Thread.current != @parser_thread
369
+ threadblock.wait
370
+ elsif block
371
+ Jabber::debuglog("WARNING:\nCannot stop current thread in Jabber::Stream#send because it is the parser thread!")
372
+ end
373
+ end
374
+
375
+ ##
376
+ # Send an XMMP stanza with an Jabber::XMPPStanza#id. The id will be
377
+ # generated by Jabber::IdGenerator if not already set.
378
+ #
379
+ # The block will be called once: when receiving a stanza with the
380
+ # same Jabber::XMPPStanza#id. There is no need to return true to
381
+ # complete this! Instead the return value of the block will be
382
+ # returned. This is a direct result of unique request/response
383
+ # stanza identification via the id attribute.
384
+ #
385
+ # The block may be omitted. Then, the result will be the response
386
+ # stanza.
387
+ #
388
+ # Be aware that if a stanza with <tt>type='error'</tt> is received
389
+ # the function does not yield but raises an ServerError with
390
+ # the corresponding error element.
391
+ #
392
+ # Please see Stream#send for some implementational details.
393
+ #
394
+ # Please read the note about nesting at Stream#send
395
+ # xml:: [XMPPStanza]
396
+ def send_with_id(xml, &block)
397
+ if xml.id.nil?
398
+ xml.id = Jabber::IdGenerator.instance.generate_id
399
+ end
400
+
401
+ res = nil
402
+ error = nil
403
+ send(xml) do |received|
404
+ if received.kind_of? XMPPStanza and received.id == xml.id
405
+ if received.type == :error
406
+ error = (received.error ? received.error : ErrorResponse.new)
407
+ true
408
+ elsif block_given?
409
+ res = yield(received)
410
+ true
411
+ else
412
+ res = received
413
+ true
414
+ end
415
+ else
416
+ false
417
+ end
418
+ end
419
+
420
+ unless error.nil?
421
+ raise ServerError.new(error)
422
+ end
423
+
424
+ res
425
+ end
426
+
427
+ ##
428
+ # Adds a callback block to process received XML messages, these
429
+ # will be handled before any blocks given to Stream#send or other
430
+ # callbacks.
431
+ #
432
+ # priority:: [Integer] The callback's priority, the higher, the sooner
433
+ # ref:: [String] The callback's reference
434
+ # &block:: [Block] The optional block
435
+ def add_xml_callback(priority = 0, ref = nil, &block)
436
+ @xmlcbs.add(priority, ref, block)
437
+ end
438
+
439
+ ##
440
+ # Delete an XML-messages callback
441
+ #
442
+ # ref:: [String] The reference of the callback to delete
443
+ def delete_xml_callback(ref)
444
+ @xmlcbs.delete(ref)
445
+ end
446
+
447
+ ##
448
+ # Adds a callback block to process received Messages
449
+ #
450
+ # priority:: [Integer] The callback's priority, the higher, the sooner
451
+ # ref:: [String] The callback's reference
452
+ # &block:: [Block] The optional block
453
+ def add_message_callback(priority = 0, ref = nil, &block)
454
+ @messagecbs.add(priority, ref, block)
455
+ end
456
+
457
+ ##
458
+ # Delete an Message callback
459
+ #
460
+ # ref:: [String] The reference of the callback to delete
461
+ def delete_message_callback(ref)
462
+ @messagecbs.delete(ref)
463
+ end
464
+
465
+ ##
466
+ # Adds a callback block to process received Stanzas
467
+ #
468
+ # priority:: [Integer] The callback's priority, the higher, the sooner
469
+ # ref:: [String] The callback's reference
470
+ # &block:: [Block] The optional block
471
+ def add_stanza_callback(priority = 0, ref = nil, &block)
472
+ @stanzacbs.add(priority, ref, block)
473
+ end
474
+
475
+ ##
476
+ # Delete a Stanza callback
477
+ #
478
+ # ref:: [String] The reference of the callback to delete
479
+ def delete_stanza_callback(ref)
480
+ @stanzacbs.delete(ref)
481
+ end
482
+
483
+ ##
484
+ # Adds a callback block to process received Presences
485
+ #
486
+ # priority:: [Integer] The callback's priority, the higher, the sooner
487
+ # ref:: [String] The callback's reference
488
+ # &block:: [Block] The optional block
489
+ def add_presence_callback(priority = 0, ref = nil, &block)
490
+ @presencecbs.add(priority, ref, block)
491
+ end
492
+
493
+ ##
494
+ # Delete a Presence callback
495
+ #
496
+ # ref:: [String] The reference of the callback to delete
497
+ def delete_presence_callback(ref)
498
+ @presencecbs.delete(ref)
499
+ end
500
+
501
+ ##
502
+ # Adds a callback block to process received Iqs
503
+ #
504
+ # priority:: [Integer] The callback's priority, the higher, the sooner
505
+ # ref:: [String] The callback's reference
506
+ # &block:: [Block] The optional block
507
+ def add_iq_callback(priority = 0, ref = nil, &block)
508
+ @iqcbs.add(priority, ref, block)
509
+ end
510
+
511
+ ##
512
+ # Delete an Iq callback
513
+ #
514
+ # ref:: [String] The reference of the callback to delete
515
+ #
516
+ def delete_iq_callback(ref)
517
+ @iqcbs.delete(ref)
518
+ end
519
+ ##
520
+ # Closes the connection to the Jabber service
521
+ def close
522
+ close!
523
+ end
524
+
525
+ def close!
526
+ @parser_thread.kill if @parser_thread
527
+ @fd.close if @fd and !@fd.closed?
528
+ @status = DISCONNECTED
529
+ end
530
+ end
531
+ end
@@ -0,0 +1,81 @@
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 XMPPStanza 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(XMPPStanza)] 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
+ # Handling <stream:stream> not only when it is being
47
+ # received as a top-level tag but also as a child of the
48
+ # top-level element itself. This way, we handle stream
49
+ # restarts (ie. after SASL authentication).
50
+ if @current.name == 'stream' and @current.parent.nil?
51
+ @started = true
52
+ @listener.receive(@current)
53
+ @current = nil
54
+ end
55
+ end
56
+
57
+ parser.listen( :end_element ) do |uri, localname, qname|
58
+ if qname == 'stream:stream' and @current.nil?
59
+ @started = false
60
+ @listener.parser_end
61
+ else
62
+ @listener.receive(@current) unless @current.parent
63
+ @current = @current.parent
64
+ end
65
+ end
66
+
67
+ parser.listen( :characters ) do | text |
68
+ @current.add(REXML::Text.new(text.to_s, @current.whitespace, nil, true)) if @current
69
+ end
70
+
71
+ parser.listen( :cdata ) do | text |
72
+ @current.add(REXML::CData.new(text)) if @current
73
+ end
74
+
75
+ parser.parse
76
+ rescue REXML::ParseException => e
77
+ @listener.parse_failure(e)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,58 @@
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'
6
+ require 'xmpp4r/pubsub'
7
+ require 'xmpp4r/tune/tune'
8
+
9
+ module Jabber
10
+ module UserTune
11
+ ##
12
+ # A Helper for XEP-0118 User Tune
13
+ #
14
+ # Use this helper to send user tunes, or receive them from a
15
+ # specified jid. Described at http://www.xmpp.org/extensions/xep-0118.html
16
+ #
17
+ # For example:
18
+ # <pre>
19
+ # h = UserTune::Helper( @client, 'radio1@hug.hellomatty.com' )
20
+ # h.add_usertune_callback do |tune|
21
+ # puts "Now playing: #{tune.title} by #{tune.artist}"
22
+ # end
23
+ # </pre>
24
+ #
25
+ # Also see the examples provided.
26
+ class Helper < PubSub::ServiceHelper
27
+ ##
28
+ # Send out the tune currently playing.
29
+ #
30
+ # track:: [Jabber::UserTune::Tune] the tune currently playing
31
+ def now_playing(track)
32
+ item = Jabber::PubSub::Item.new()
33
+ item.add(track)
34
+
35
+ publish_item_to(NS_USERTUNE, item)
36
+ end
37
+
38
+ ##
39
+ # Use this method to indicate that you have stopped playing
40
+ # a tune.
41
+ def stop_playing
42
+ now_playing(Jabber::UserTune::Tune.new())
43
+ end
44
+
45
+ ##
46
+ # Add a callback that will be invoked when a tune is received
47
+ # from the jid specified when you constructed the Helper.
48
+ def add_usertune_callback(prio = 200, ref = nil, &block)
49
+ add_event_callback(prio, ref) do |event|
50
+ tune = event.first_element('items/item/tune')
51
+ if tune
52
+ block.call(tune)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end