ln-xmpp4r 0.5

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 (215) hide show
  1. data/CHANGELOG +91 -0
  2. data/COPYING +340 -0
  3. data/LICENSE +59 -0
  4. data/README.rdoc +113 -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.rb +116 -0
  51. data/lib/xmpp4r/base64.rb +32 -0
  52. data/lib/xmpp4r/bytestreams.rb +15 -0
  53. data/lib/xmpp4r/bytestreams/helper/filetransfer.rb +319 -0
  54. data/lib/xmpp4r/bytestreams/helper/ibb/base.rb +257 -0
  55. data/lib/xmpp4r/bytestreams/helper/ibb/initiator.rb +31 -0
  56. data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +54 -0
  57. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb +152 -0
  58. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb +86 -0
  59. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb +198 -0
  60. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb +65 -0
  61. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +79 -0
  62. data/lib/xmpp4r/bytestreams/iq/bytestreams.rb +170 -0
  63. data/lib/xmpp4r/bytestreams/iq/si.rb +206 -0
  64. data/lib/xmpp4r/callbacks.rb +133 -0
  65. data/lib/xmpp4r/caps.rb +1 -0
  66. data/lib/xmpp4r/caps/c.rb +67 -0
  67. data/lib/xmpp4r/caps/helper/generator.rb +160 -0
  68. data/lib/xmpp4r/caps/helper/helper.rb +84 -0
  69. data/lib/xmpp4r/client.rb +344 -0
  70. data/lib/xmpp4r/command/helper/responder.rb +53 -0
  71. data/lib/xmpp4r/command/iq/command.rb +154 -0
  72. data/lib/xmpp4r/component.rb +103 -0
  73. data/lib/xmpp4r/connection.rb +223 -0
  74. data/lib/xmpp4r/dataforms.rb +5 -0
  75. data/lib/xmpp4r/dataforms/x/data.rb +297 -0
  76. data/lib/xmpp4r/debuglog.rb +63 -0
  77. data/lib/xmpp4r/delay.rb +5 -0
  78. data/lib/xmpp4r/delay/x/delay.rb +99 -0
  79. data/lib/xmpp4r/discovery.rb +8 -0
  80. data/lib/xmpp4r/discovery/helper/helper.rb +58 -0
  81. data/lib/xmpp4r/discovery/helper/responder.rb +165 -0
  82. data/lib/xmpp4r/discovery/iq/discoinfo.rb +211 -0
  83. data/lib/xmpp4r/discovery/iq/discoitems.rb +147 -0
  84. data/lib/xmpp4r/errors.rb +284 -0
  85. data/lib/xmpp4r/feature_negotiation.rb +5 -0
  86. data/lib/xmpp4r/feature_negotiation/iq/feature.rb +28 -0
  87. data/lib/xmpp4r/framework/base.rb +55 -0
  88. data/lib/xmpp4r/framework/bot.rb +148 -0
  89. data/lib/xmpp4r/httpbinding.rb +5 -0
  90. data/lib/xmpp4r/httpbinding/client.rb +275 -0
  91. data/lib/xmpp4r/idgenerator.rb +37 -0
  92. data/lib/xmpp4r/iq.rb +221 -0
  93. data/lib/xmpp4r/jid.rb +167 -0
  94. data/lib/xmpp4r/last.rb +2 -0
  95. data/lib/xmpp4r/last/helper/helper.rb +37 -0
  96. data/lib/xmpp4r/last/iq/last.rb +67 -0
  97. data/lib/xmpp4r/location.rb +2 -0
  98. data/lib/xmpp4r/location/helper/helper.rb +56 -0
  99. data/lib/xmpp4r/location/location.rb +179 -0
  100. data/lib/xmpp4r/message.rb +180 -0
  101. data/lib/xmpp4r/muc.rb +14 -0
  102. data/lib/xmpp4r/muc/helper/mucbrowser.rb +92 -0
  103. data/lib/xmpp4r/muc/helper/mucclient.rb +462 -0
  104. data/lib/xmpp4r/muc/helper/simplemucclient.rb +332 -0
  105. data/lib/xmpp4r/muc/iq/mucadmin.rb +23 -0
  106. data/lib/xmpp4r/muc/iq/mucadminitem.rb +20 -0
  107. data/lib/xmpp4r/muc/iq/mucowner.rb +15 -0
  108. data/lib/xmpp4r/muc/item.rb +143 -0
  109. data/lib/xmpp4r/muc/x/muc.rb +70 -0
  110. data/lib/xmpp4r/muc/x/mucuserinvite.rb +60 -0
  111. data/lib/xmpp4r/muc/x/mucuseritem.rb +36 -0
  112. data/lib/xmpp4r/presence.rb +232 -0
  113. data/lib/xmpp4r/pubsub.rb +8 -0
  114. data/lib/xmpp4r/pubsub/children/configuration.rb +86 -0
  115. data/lib/xmpp4r/pubsub/children/event.rb +49 -0
  116. data/lib/xmpp4r/pubsub/children/item.rb +35 -0
  117. data/lib/xmpp4r/pubsub/children/items.rb +53 -0
  118. data/lib/xmpp4r/pubsub/children/node_config.rb +48 -0
  119. data/lib/xmpp4r/pubsub/children/publish.rb +38 -0
  120. data/lib/xmpp4r/pubsub/children/retract.rb +41 -0
  121. data/lib/xmpp4r/pubsub/children/subscription.rb +62 -0
  122. data/lib/xmpp4r/pubsub/children/subscription_config.rb +67 -0
  123. data/lib/xmpp4r/pubsub/children/unsubscribe.rb +48 -0
  124. data/lib/xmpp4r/pubsub/helper/nodebrowser.rb +129 -0
  125. data/lib/xmpp4r/pubsub/helper/nodehelper.rb +156 -0
  126. data/lib/xmpp4r/pubsub/helper/oauth_service_helper.rb +90 -0
  127. data/lib/xmpp4r/pubsub/helper/servicehelper.rb +456 -0
  128. data/lib/xmpp4r/pubsub/iq/pubsub.rb +19 -0
  129. data/lib/xmpp4r/query.rb +15 -0
  130. data/lib/xmpp4r/reliable.rb +168 -0
  131. data/lib/xmpp4r/rexmladdons.rb +157 -0
  132. data/lib/xmpp4r/roster.rb +7 -0
  133. data/lib/xmpp4r/roster/helper/roster.rb +522 -0
  134. data/lib/xmpp4r/roster/iq/roster.rb +215 -0
  135. data/lib/xmpp4r/roster/x/roster.rb +138 -0
  136. data/lib/xmpp4r/rpc.rb +2 -0
  137. data/lib/xmpp4r/rpc/helper/client.rb +123 -0
  138. data/lib/xmpp4r/rpc/helper/server.rb +74 -0
  139. data/lib/xmpp4r/rpc/helper/xmlrpcaddons.rb +67 -0
  140. data/lib/xmpp4r/rpc/iq/rpc.rb +23 -0
  141. data/lib/xmpp4r/sasl.rb +248 -0
  142. data/lib/xmpp4r/semaphore.rb +38 -0
  143. data/lib/xmpp4r/stream.rb +599 -0
  144. data/lib/xmpp4r/streamparser.rb +85 -0
  145. data/lib/xmpp4r/test/listener_mocker.rb +118 -0
  146. data/lib/xmpp4r/tune.rb +2 -0
  147. data/lib/xmpp4r/tune/helper/helper.rb +58 -0
  148. data/lib/xmpp4r/tune/tune.rb +113 -0
  149. data/lib/xmpp4r/vcard.rb +6 -0
  150. data/lib/xmpp4r/vcard/helper/vcard.rb +84 -0
  151. data/lib/xmpp4r/vcard/iq/vcard.rb +109 -0
  152. data/lib/xmpp4r/version.rb +7 -0
  153. data/lib/xmpp4r/version/helper/responder.rb +72 -0
  154. data/lib/xmpp4r/version/helper/simpleresponder.rb +44 -0
  155. data/lib/xmpp4r/version/iq/version.rb +105 -0
  156. data/lib/xmpp4r/x.rb +37 -0
  157. data/lib/xmpp4r/xhtml.rb +1 -0
  158. data/lib/xmpp4r/xhtml/html.rb +115 -0
  159. data/lib/xmpp4r/xmpp4r.rb +20 -0
  160. data/lib/xmpp4r/xmppelement.rb +168 -0
  161. data/lib/xmpp4r/xmppstanza.rb +162 -0
  162. data/setup.rb +1586 -0
  163. data/test/bytestreams/tc_ibb.rb +188 -0
  164. data/test/bytestreams/tc_socks5bytestreams.rb +114 -0
  165. data/test/caps/tc_helper.rb +158 -0
  166. data/test/dataforms/tc_data.rb +81 -0
  167. data/test/delay/tc_xdelay.rb +51 -0
  168. data/test/discovery/tc_responder.rb +91 -0
  169. data/test/last/tc_helper.rb +75 -0
  170. data/test/lib/assert_equal_xml.rb +14 -0
  171. data/test/lib/clienttester.rb +149 -0
  172. data/test/muc/tc_muc_mucclient.rb +834 -0
  173. data/test/muc/tc_muc_simplemucclient.rb +114 -0
  174. data/test/muc/tc_mucowner.rb +50 -0
  175. data/test/pubsub/tc_helper.rb +785 -0
  176. data/test/pubsub/tc_nodeconfig.rb +61 -0
  177. data/test/pubsub/tc_subscriptionconfig.rb +41 -0
  178. data/test/reliable/tc_disconnect_cleanup.rb +334 -0
  179. data/test/reliable/tc_disconnect_exception.rb +37 -0
  180. data/test/reliable/tc_listener_mocked_test.rb +68 -0
  181. data/test/reliable/tc_reliable_connection.rb +31 -0
  182. data/test/roster/tc_helper.rb +524 -0
  183. data/test/roster/tc_iqqueryroster.rb +173 -0
  184. data/test/roster/tc_xroster.rb +73 -0
  185. data/test/rpc/tc_helper.rb +96 -0
  186. data/test/tc_callbacks.rb +129 -0
  187. data/test/tc_class_names.rb +146 -0
  188. data/test/tc_client.rb +30 -0
  189. data/test/tc_errors.rb +146 -0
  190. data/test/tc_idgenerator.rb +30 -0
  191. data/test/tc_iq.rb +113 -0
  192. data/test/tc_iqquery.rb +31 -0
  193. data/test/tc_jid.rb +204 -0
  194. data/test/tc_message.rb +131 -0
  195. data/test/tc_presence.rb +150 -0
  196. data/test/tc_rexml.rb +139 -0
  197. data/test/tc_stream.rb +167 -0
  198. data/test/tc_streamComponent.rb +95 -0
  199. data/test/tc_streamError.rb +129 -0
  200. data/test/tc_streamSend.rb +59 -0
  201. data/test/tc_streamparser.rb +125 -0
  202. data/test/tc_xmppstanza.rb +135 -0
  203. data/test/ts_xmpp4r.rb +44 -0
  204. data/test/tune/tc_helper_recv.rb +82 -0
  205. data/test/tune/tc_helper_send.rb +74 -0
  206. data/test/tune/tc_tune.rb +79 -0
  207. data/test/vcard/tc_helper.rb +49 -0
  208. data/test/vcard/tc_iqvcard.rb +62 -0
  209. data/test/version/tc_helper.rb +60 -0
  210. data/test/version/tc_iqqueryversion.rb +97 -0
  211. data/test/xhtml/tc_html.rb +41 -0
  212. data/tools/gen_requires.bash +31 -0
  213. data/tools/xmpp4r-gemspec-test.rb +11 -0
  214. data/xmpp4r.gemspec +238 -0
  215. metadata +280 -0
@@ -0,0 +1,599 @@
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
+ # number of stanzas currently being processed
39
+ attr_reader :processing
40
+
41
+ ##
42
+ # Initialize a new stream
43
+ def initialize
44
+ @fd = nil
45
+ @status = DISCONNECTED
46
+ @xmlcbs = CallbackList.new
47
+ @stanzacbs = CallbackList.new
48
+ @messagecbs = CallbackList.new
49
+ @iqcbs = CallbackList.new
50
+ @presencecbs = CallbackList.new
51
+ @send_lock = Mutex.new
52
+ @last_send = Time.now
53
+ @exception_block = nil
54
+ @tbcbmutex = Mutex.new
55
+ @threadblocks = []
56
+ @wakeup_thread = nil
57
+ @streamid = nil
58
+ @streamns = 'jabber:client'
59
+ @features_sem = Semaphore.new
60
+ @parser_thread = nil
61
+ @processing = 0
62
+ end
63
+
64
+ ##
65
+ # Start the XML parser on the fd
66
+ def start(fd)
67
+ @stream_mechanisms = []
68
+ @stream_features = {}
69
+
70
+ @fd = fd
71
+ @parser = StreamParser.new(@fd, self)
72
+ @parser_thread = Thread.new do
73
+ Thread.current.abort_on_exception = true
74
+ begin
75
+ @parser.parse
76
+ Jabber::debuglog("DISCONNECTED\n")
77
+
78
+ if @exception_block
79
+ Thread.new { close!; @exception_block.call(nil, self, :disconnected) }
80
+ else
81
+ close!
82
+ end
83
+ rescue Exception => e
84
+ Jabber::warnlog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
85
+
86
+ if @exception_block
87
+ Thread.new do
88
+ Thread.current.abort_on_exception = true
89
+ close
90
+ @exception_block.call(e, self, :start)
91
+ end
92
+ else
93
+ Jabber::warnlog "Exception caught in Parser thread! (#{e.class})\n#{e.backtrace.join("\n")}"
94
+ close!
95
+ raise
96
+ end
97
+ end
98
+ end
99
+
100
+ @status = CONNECTED
101
+ end
102
+
103
+ def stop
104
+ @parser_thread.kill
105
+ @parser = nil
106
+ end
107
+
108
+ ##
109
+ # Mounts a block to handle exceptions if they occur during the
110
+ # poll send. This will likely be the first indication that
111
+ # the socket dropped in a Jabber Session.
112
+ #
113
+ # The block has to take three arguments:
114
+ # * the Exception
115
+ # * the Jabber::Stream object (self)
116
+ # * a symbol where it happened, namely :start, :parser, :sending and :end
117
+ def on_exception(&block)
118
+ @exception_block = block
119
+ end
120
+
121
+ ##
122
+ # This method is called by the parser when a failure occurs
123
+ def parse_failure(e)
124
+ Jabber::warnlog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
125
+
126
+ # A new thread has to be created because close will cause the thread
127
+ # to commit suicide(???)
128
+ if @exception_block
129
+ # New thread, because close will kill the current thread
130
+ Thread.new do
131
+ Thread.current.abort_on_exception = true
132
+ close
133
+ @exception_block.call(e, self, :parser)
134
+ end
135
+ else
136
+ Jabber::warnlog "Stream#parse_failure was called by XML parser. Dumping " +
137
+ "backtrace...\n" + e.exception + "\n#{e.backtrace.join("\n")}"
138
+ close
139
+ raise
140
+ end
141
+ end
142
+
143
+ ##
144
+ # This method is called by the parser upon receiving <tt></stream:stream></tt>
145
+ def parser_end
146
+ if @exception_block
147
+ Thread.new do
148
+ Thread.current.abort_on_exception = true
149
+ close
150
+ @exception_block.call(nil, self, :close)
151
+ end
152
+ else
153
+ close
154
+ end
155
+ end
156
+
157
+ ##
158
+ # Returns if this connection is connected to a Jabber service
159
+ # return:: [Boolean] Connection status
160
+ def is_connected?
161
+ return @status == CONNECTED
162
+ end
163
+
164
+ ##
165
+ # Returns if this connection is NOT connected to a Jabber service
166
+ #
167
+ # return:: [Boolean] Connection status
168
+ def is_disconnected?
169
+ return @status == DISCONNECTED
170
+ end
171
+
172
+ ##
173
+ # Processes a received REXML::Element and executes
174
+ # registered thread blocks and filters against it.
175
+ #
176
+ # element:: [REXML::Element] The received element
177
+ def receive(element)
178
+ @tbcbmutex.synchronize { @processing += 1 }
179
+ Jabber::debuglog("RECEIVED:\n#{element.to_s}")
180
+
181
+ if element.namespace('').to_s == '' # REXML namespaces are always strings
182
+ element.add_namespace(@streamns)
183
+ end
184
+
185
+ case element.prefix
186
+ when 'stream'
187
+ case element.name
188
+ when 'stream'
189
+ stanza = element
190
+ @streamid = element.attributes['id']
191
+ @streamns = element.namespace('') if element.namespace('')
192
+
193
+ # Hack: component streams are basically client streams.
194
+ # Someday we may want to create special stanza classes
195
+ # for components/s2s deriving from normal stanzas but
196
+ # posessing these namespaces
197
+ @streamns = 'jabber:client' if @streamns == 'jabber:component:accept'
198
+
199
+ unless element.attributes['version'] # isn't XMPP compliant, so
200
+ Jabber::debuglog("FEATURES: server not XMPP compliant, will not wait for features")
201
+ @features_sem.run # don't wait for <stream:features/>
202
+ end
203
+ when 'features'
204
+ stanza = element
205
+ element.each { |e|
206
+ if e.name == 'mechanisms' and e.namespace == 'urn:ietf:params:xml:ns:xmpp-sasl'
207
+ e.each_element('mechanism') { |mech|
208
+ @stream_mechanisms.push(mech.text)
209
+ }
210
+ else
211
+ @stream_features[e.name] = e.namespace
212
+ end
213
+ }
214
+ Jabber::debuglog("FEATURES: received")
215
+ @features_sem.run
216
+ else
217
+ stanza = element
218
+ end
219
+ else
220
+ # Any stanza, classes are registered by XMPPElement::name_xmlns
221
+ begin
222
+ stanza = XMPPStanza::import(element)
223
+ rescue NoNameXmlnsRegistered
224
+ stanza = element
225
+ end
226
+ end
227
+
228
+ if @xmlcbs.process(stanza)
229
+ @tbcbmutex.synchronize { @processing -= 1 }
230
+ return true
231
+ end
232
+
233
+ # Iterate through blocked threads (= waiting for an answer)
234
+ #
235
+ # We're dup'ping the @threadblocks here, so that we won't end up in an
236
+ # endless loop if Stream#send is being nested. That means, the nested
237
+ # threadblock won't receive the stanza currently processed, but the next
238
+ # one.
239
+ threadblocks = nil
240
+ @tbcbmutex.synchronize do
241
+ threadblocks = @threadblocks.dup
242
+ end
243
+ threadblocks.each { |threadblock|
244
+ exception = nil
245
+ r = false
246
+ begin
247
+ r = threadblock.call(stanza)
248
+ rescue Exception => e
249
+ exception = e
250
+ end
251
+
252
+ if r == true
253
+ @tbcbmutex.synchronize do
254
+ @threadblocks.delete(threadblock)
255
+ end
256
+ threadblock.wakeup
257
+ @tbcbmutex.synchronize { @processing -= 1 }
258
+ return true
259
+ elsif exception
260
+ @tbcbmutex.synchronize do
261
+ @threadblocks.delete(threadblock)
262
+ end
263
+ threadblock.raise(exception)
264
+ end
265
+ }
266
+
267
+ Jabber::debuglog("PROCESSING:\n#{stanza.to_s} (#{stanza.class})")
268
+ Jabber::debuglog("TRYING stanzacbs...")
269
+ if @stanzacbs.process(stanza)
270
+ @tbcbmutex.synchronize { @processing -= 1 }
271
+ return true
272
+ end
273
+ r = false
274
+ Jabber::debuglog("TRYING message/iq/presence/cbs...")
275
+ case stanza
276
+ when Message
277
+ r = @messagecbs.process(stanza)
278
+ when Iq
279
+ r = @iqcbs.process(stanza)
280
+ when Presence
281
+ r = @presencecbs.process(stanza)
282
+ end
283
+ @tbcbmutex.synchronize { @processing -= 1 }
284
+ return r
285
+ end
286
+
287
+ ##
288
+ # Get the list of iq callbacks.
289
+ def iq_callbacks
290
+ @iqcbs
291
+ end
292
+
293
+ ##
294
+ # Get the list of message callbacks.
295
+ def message_callbacks
296
+ @messagecbs
297
+ end
298
+
299
+ ##
300
+ # Get the list of presence callbacks.
301
+ def presence_callbacks
302
+ @presencecbs
303
+ end
304
+
305
+ ##
306
+ # Get the list of stanza callbacks.
307
+ def stanza_callbacks
308
+ @stanzacbs
309
+ end
310
+
311
+ ##
312
+ # Get the list of xml callbacks.
313
+ def xml_callbacks
314
+ @xmlcbs
315
+ end
316
+
317
+ ##
318
+ # This is used by Jabber::Stream internally to
319
+ # keep track of any blocks which were passed to
320
+ # Stream#send.
321
+ class ThreadBlock
322
+ def initialize(block)
323
+ @block = block
324
+ @waiter = Semaphore.new
325
+ @exception = nil
326
+ end
327
+ def call(*args)
328
+ @block.call(*args)
329
+ end
330
+ def wait
331
+ @waiter.wait
332
+ raise @exception if @exception
333
+ end
334
+ def wakeup
335
+ @waiter.run
336
+ end
337
+ def raise(exception)
338
+ @exception = exception
339
+ @waiter.run
340
+ end
341
+ end
342
+
343
+ def send_data(data)
344
+ @send_lock.synchronize do
345
+ @last_send = Time.now
346
+ @fd << data
347
+ @fd.flush
348
+ end
349
+ end
350
+
351
+ ##
352
+ # Sends XML data to the socket and (optionally) waits
353
+ # to process received data.
354
+ #
355
+ # Do not invoke this in a callback but in a seperate thread
356
+ # because we may not suspend the parser-thread (in whose
357
+ # context callbacks are executed).
358
+ #
359
+ # xml:: [String] The xml data to send
360
+ # &block:: [Block] The optional block
361
+ def send(xml, &block)
362
+ Jabber::debuglog("SENDING:\n#{xml}")
363
+ if block
364
+ threadblock = ThreadBlock.new(block)
365
+ @tbcbmutex.synchronize do
366
+ @threadblocks.unshift(threadblock)
367
+ end
368
+ end
369
+ begin
370
+ # Temporarily remove stanza's namespace to
371
+ # reduce bandwidth consumption
372
+ if xml.kind_of? XMPPStanza and xml.namespace == 'jabber:client' and
373
+ xml.prefix != 'stream' and xml.name != 'stream'
374
+ xml.delete_namespace
375
+ send_data(xml.to_s)
376
+ xml.add_namespace(@streamns)
377
+ else
378
+ send_data(xml.to_s)
379
+ end
380
+ rescue Exception => e
381
+ Jabber::warnlog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
382
+
383
+ if @exception_block
384
+ Thread.new do
385
+ Thread.current.abort_on_exception = true
386
+ close!
387
+ @exception_block.call(e, self, :sending)
388
+ end
389
+ else
390
+ Jabber::warnlog "Exception caught while sending! (#{e.class})\n#{e.backtrace.join("\n")}"
391
+ close!
392
+ raise
393
+ end
394
+ end
395
+ # The parser thread might be running this (think of a callback running send())
396
+ # If this is the case, we mustn't stop (or we would cause a deadlock)
397
+ if block and Thread.current != @parser_thread
398
+ threadblock.wait
399
+ elsif block
400
+ Jabber::warnlog("WARNING:\nCannot stop current thread in Jabber::Stream#send because it is the parser thread!")
401
+ end
402
+ end
403
+
404
+ ##
405
+ # Send an XMMP stanza with an Jabber::XMPPStanza#id. The id will be
406
+ # generated by Jabber::IdGenerator if not already set.
407
+ #
408
+ # The block will be called once: when receiving a stanza with the
409
+ # same Jabber::XMPPStanza#id. There is no need to return true to
410
+ # complete this! Instead the return value of the block will be
411
+ # returned. This is a direct result of unique request/response
412
+ # stanza identification via the id attribute.
413
+ #
414
+ # The block may be omitted. Then, the result will be the response
415
+ # stanza.
416
+ #
417
+ # Be aware that if a stanza with <tt>type='error'</tt> is received
418
+ # the function does not yield but raises an ServerError with
419
+ # the corresponding error element.
420
+ #
421
+ # Please see Stream#send for some implementational details.
422
+ #
423
+ # Please read the note about nesting at Stream#send
424
+ # xml:: [XMPPStanza]
425
+ def send_with_id(xml, &block)
426
+ if xml.id.nil?
427
+ xml.id = Jabber::IdGenerator.instance.generate_id
428
+ end
429
+
430
+ res = nil
431
+ error = nil
432
+ send(xml) do |received|
433
+ if received.kind_of? XMPPStanza and received.id == xml.id
434
+ if received.type == :error
435
+ error = (received.error ? received.error : ErrorResponse.new)
436
+ true
437
+ elsif block_given?
438
+ res = yield(received)
439
+ true
440
+ else
441
+ res = received
442
+ true
443
+ end
444
+ else
445
+ false
446
+ end
447
+ end
448
+
449
+ unless error.nil?
450
+ raise ServerError.new(error)
451
+ end
452
+
453
+ res
454
+ end
455
+
456
+ ##
457
+ # Adds a callback block to process received XML messages, these
458
+ # will be handled before any blocks given to Stream#send or other
459
+ # callbacks.
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_xml_callback(priority = 0, ref = nil, &block)
465
+ @tbcbmutex.synchronize do
466
+ @xmlcbs.add(priority, ref, block)
467
+ end
468
+ end
469
+
470
+ ##
471
+ # Delete an XML-messages callback
472
+ #
473
+ # ref:: [String] The reference of the callback to delete
474
+ def delete_xml_callback(ref)
475
+ @tbcbmutex.synchronize do
476
+ @xmlcbs.delete(ref)
477
+ end
478
+ end
479
+
480
+ ##
481
+ # Adds a callback block to process received Messages
482
+ #
483
+ # priority:: [Integer] The callback's priority, the higher, the sooner
484
+ # ref:: [String] The callback's reference
485
+ # &block:: [Block] The optional block
486
+ def add_message_callback(priority = 0, ref = nil, &block)
487
+ @tbcbmutex.synchronize do
488
+ @messagecbs.add(priority, ref, block)
489
+ end
490
+ end
491
+
492
+ ##
493
+ # Delete an Message callback
494
+ #
495
+ # ref:: [String] The reference of the callback to delete
496
+ def delete_message_callback(ref)
497
+ @tbcbmutex.synchronize do
498
+ @messagecbs.delete(ref)
499
+ end
500
+ end
501
+
502
+ ##
503
+ # Adds a callback block to process received Stanzas
504
+ #
505
+ # priority:: [Integer] The callback's priority, the higher, the sooner
506
+ # ref:: [String] The callback's reference
507
+ # &block:: [Block] The optional block
508
+ def add_stanza_callback(priority = 0, ref = nil, &block)
509
+ @tbcbmutex.synchronize do
510
+ @stanzacbs.add(priority, ref, block)
511
+ end
512
+ end
513
+
514
+ ##
515
+ # Delete a Stanza callback
516
+ #
517
+ # ref:: [String] The reference of the callback to delete
518
+ def delete_stanza_callback(ref)
519
+ @tbcbmutex.synchronize do
520
+ @stanzacbs.delete(ref)
521
+ end
522
+ end
523
+
524
+ ##
525
+ # Adds a callback block to process received Presences
526
+ #
527
+ # priority:: [Integer] The callback's priority, the higher, the sooner
528
+ # ref:: [String] The callback's reference
529
+ # &block:: [Block] The optional block
530
+ def add_presence_callback(priority = 0, ref = nil, &block)
531
+ @tbcbmutex.synchronize do
532
+ @presencecbs.add(priority, ref, block)
533
+ end
534
+ end
535
+
536
+ ##
537
+ # Delete a Presence callback
538
+ #
539
+ # ref:: [String] The reference of the callback to delete
540
+ def delete_presence_callback(ref)
541
+ @tbcbmutex.synchronize do
542
+ @presencecbs.delete(ref)
543
+ end
544
+ end
545
+
546
+ ##
547
+ # Adds a callback block to process received Iqs
548
+ #
549
+ # priority:: [Integer] The callback's priority, the higher, the sooner
550
+ # ref:: [String] The callback's reference
551
+ # &block:: [Block] The optional block
552
+ def add_iq_callback(priority = 0, ref = nil, &block)
553
+ @tbcbmutex.synchronize do
554
+ @iqcbs.add(priority, ref, block)
555
+ end
556
+ end
557
+
558
+ ##
559
+ # Delete an Iq callback
560
+ #
561
+ # ref:: [String] The reference of the callback to delete
562
+ #
563
+ def delete_iq_callback(ref)
564
+ @tbcbmutex.synchronize do
565
+ @iqcbs.delete(ref)
566
+ end
567
+ end
568
+ ##
569
+ # Closes the connection to the Jabber service
570
+ def close
571
+ close!
572
+ end
573
+
574
+ def close!
575
+ pr = 1
576
+ n = 0
577
+ # In some cases, we might lost count of some stanzas
578
+ # (for example, if the handler raises an exception)
579
+ # so we can't block forever.
580
+ while pr > 0 and n <= 1000
581
+ @tbcbmutex.synchronize { pr = @processing }
582
+ if pr > 0
583
+ n += 1
584
+ Jabber::debuglog("TRYING TO CLOSE, STILL PROCESSING #{pr} STANZAS")
585
+ #puts("TRYING TO CLOSE, STILL PROCESSING #{pr} STANZAS")
586
+ Thread::pass
587
+ end
588
+ end
589
+
590
+ # Order Matters here! If this method is called from within
591
+ # @parser_thread then killing @parser_thread first would
592
+ # mean the other parts of the method fail to execute.
593
+ # That would be bad. So kill parser_thread last
594
+ @fd.close if @fd and !@fd.closed?
595
+ @status = DISCONNECTED
596
+ @parser_thread.kill if @parser_thread
597
+ end
598
+ end
599
+ end