xmpp4r 0.4 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +8 -0
- data/README.rdoc +4 -1
- data/Rakefile +10 -20
- data/data/doc/xmpp4r/examples/advanced/versionpoll.rb +20 -1
- data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +7 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +7 -1
- data/lib/xmpp4r/callbacks.rb +9 -0
- data/lib/xmpp4r/caps/c.rb +14 -0
- data/lib/xmpp4r/caps/helper/helper.rb +1 -4
- data/lib/xmpp4r/client.rb +42 -15
- data/lib/xmpp4r/connection.rb +7 -3
- data/lib/xmpp4r/debuglog.rb +22 -1
- data/lib/xmpp4r/discovery.rb +1 -0
- data/lib/xmpp4r/discovery/helper/helper.rb +58 -0
- data/lib/xmpp4r/discovery/iq/discoinfo.rb +2 -2
- data/lib/xmpp4r/discovery/iq/discoitems.rb +2 -2
- data/lib/xmpp4r/errors.rb +5 -2
- data/lib/xmpp4r/httpbinding/client.rb +9 -19
- data/lib/xmpp4r/last.rb +2 -0
- data/lib/xmpp4r/last/helper/helper.rb +37 -0
- data/lib/xmpp4r/last/iq/last.rb +67 -0
- data/lib/xmpp4r/location.rb +2 -0
- data/lib/xmpp4r/location/helper/helper.rb +56 -0
- data/lib/xmpp4r/location/location.rb +179 -0
- data/lib/xmpp4r/message.rb +32 -0
- data/lib/xmpp4r/presence.rb +1 -1
- data/lib/xmpp4r/pubsub/children/configuration.rb +1 -1
- data/lib/xmpp4r/pubsub/children/items.rb +11 -2
- data/lib/xmpp4r/pubsub/children/publish.rb +14 -0
- data/lib/xmpp4r/pubsub/children/retract.rb +41 -0
- data/lib/xmpp4r/pubsub/helper/nodebrowser.rb +2 -3
- data/lib/xmpp4r/pubsub/helper/nodehelper.rb +4 -4
- data/lib/xmpp4r/pubsub/helper/oauth_service_helper.rb +90 -0
- data/lib/xmpp4r/pubsub/helper/servicehelper.rb +58 -19
- data/lib/xmpp4r/reliable.rb +168 -0
- data/lib/xmpp4r/rexmladdons.rb +6 -0
- data/lib/xmpp4r/roster/helper/roster.rb +5 -2
- data/lib/xmpp4r/sasl.rb +19 -8
- data/lib/xmpp4r/stream.rb +133 -31
- data/lib/xmpp4r/streamparser.rb +9 -1
- data/lib/xmpp4r/test/listener_mocker.rb +118 -0
- data/lib/xmpp4r/xmpp4r.rb +3 -1
- data/test/bytestreams/tc_ibb.rb +6 -4
- data/test/bytestreams/tc_socks5bytestreams.rb +3 -2
- data/test/caps/tc_helper.rb +4 -2
- data/test/dataforms/tc_data.rb +1 -1
- data/test/last/tc_helper.rb +75 -0
- data/test/lib/clienttester.rb +43 -14
- data/test/muc/tc_muc_mucclient.rb +6 -2
- data/test/pubsub/tc_helper.rb +131 -8
- data/test/pubsub/tc_nodeconfig.rb +7 -0
- data/test/reliable/tc_disconnect_cleanup.rb +334 -0
- data/test/reliable/tc_disconnect_exception.rb +37 -0
- data/test/reliable/tc_listener_mocked_test.rb +68 -0
- data/test/reliable/tc_reliable_connection.rb +31 -0
- data/test/roster/tc_helper.rb +21 -11
- data/test/rpc/tc_helper.rb +2 -2
- data/test/tc_callbacks.rb +3 -3
- data/test/tc_message.rb +15 -0
- data/test/tc_stream.rb +59 -121
- data/test/tc_streamError.rb +2 -4
- data/test/tc_streamparser.rb +26 -13
- data/test/ts_xmpp4r.rb +0 -9
- data/test/tune/tc_helper_recv.rb +0 -2
- data/test/vcard/tc_helper.rb +1 -1
- data/xmpp4r.gemspec +31 -84
- metadata +116 -167
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb.orig +0 -62
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'xmpp4r/stream'
|
2
|
+
|
3
|
+
module Jabber
|
4
|
+
module Reliable
|
5
|
+
|
6
|
+
class Connection < Jabber::Client
|
7
|
+
def initialize(full_jid, config)
|
8
|
+
super(full_jid)
|
9
|
+
@servers = config[:servers]
|
10
|
+
@port = config[:port] || 5222
|
11
|
+
@max_retry = config[:max_retry] || 30
|
12
|
+
@retry_sleep = config[:retry_sleep] || 2
|
13
|
+
if(@servers.nil? or @servers.empty?)
|
14
|
+
@servers = [@jid.domain]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def connect
|
19
|
+
retry_count = 0
|
20
|
+
server_to_use = nil
|
21
|
+
server_pool = @servers.dup.sort{ rand <=> rand }
|
22
|
+
begin
|
23
|
+
server_to_use = server_pool.shift
|
24
|
+
server_pool.push(server_to_use)
|
25
|
+
|
26
|
+
Jabber::debuglog "timeout will be: #{@retry_sleep.to_f}"
|
27
|
+
Timeout.timeout(@retry_sleep.to_f){
|
28
|
+
Jabber::debuglog "trying to connect to #{server_to_use}"
|
29
|
+
super(server_to_use, @port)
|
30
|
+
}
|
31
|
+
|
32
|
+
Jabber::debuglog self.jid.to_s + " connected to " + server_to_use.to_s
|
33
|
+
Jabber::debuglog "out of possible servers " + @servers.inspect
|
34
|
+
rescue Exception, Timeout::Error => e
|
35
|
+
Jabber::warnlog "#{server_to_use} error: #{e.inspect}. Will attempt to reconnect in #{@retry_sleep}"
|
36
|
+
sleep(@retry_sleep.to_f)
|
37
|
+
if(retry_count >= @max_retry.to_i)
|
38
|
+
Jabber::warnlog "reached max retry count on exception, failing"
|
39
|
+
raise e
|
40
|
+
end
|
41
|
+
retry_count += 1
|
42
|
+
retry
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
class Listener
|
49
|
+
def initialize(full_jid, password, config, &block)
|
50
|
+
@on_message_block = block
|
51
|
+
@full_jid = full_jid
|
52
|
+
@config = config
|
53
|
+
@password = password
|
54
|
+
@max_retry = config[:max_retry] || 30
|
55
|
+
end
|
56
|
+
|
57
|
+
def setup_connection
|
58
|
+
@connection = Connection.new(@full_jid, @config)
|
59
|
+
if @on_message_block
|
60
|
+
@connection.add_message_callback(&@on_message_block)
|
61
|
+
else
|
62
|
+
@connection.add_message_callback do |msg|
|
63
|
+
self.on_message(msg)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
#We could just reconnect in @connection.on_exception,
|
68
|
+
#but by raising into this seperate thread, we avoid growing our stack trace
|
69
|
+
@reconnection_thread = Thread.new do
|
70
|
+
first_run = true
|
71
|
+
begin
|
72
|
+
self.start unless first_run
|
73
|
+
loop do
|
74
|
+
sleep(1)
|
75
|
+
Thread.pass
|
76
|
+
end
|
77
|
+
rescue => e
|
78
|
+
first_run = false
|
79
|
+
retry
|
80
|
+
end
|
81
|
+
end
|
82
|
+
@exception_handlers = []
|
83
|
+
@connection.on_exception do |e, connection, where_failed|
|
84
|
+
self.run_exception_handlers(e, connection, where_failed)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def run_exception_handlers(e, connection, where_failed)
|
89
|
+
@exception_handlers.each do |ex_handler|
|
90
|
+
ex_handler.call(e, connection, where_failed)
|
91
|
+
end
|
92
|
+
if where_failed == :sending
|
93
|
+
@message_to_send_on_reconnect = @message_now_sending
|
94
|
+
end
|
95
|
+
if where_failed != :close && !@connection.is_connected?
|
96
|
+
@reconnection_thread.raise(e)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_exception_handler(&block)
|
101
|
+
@exception_handlers << block
|
102
|
+
end
|
103
|
+
|
104
|
+
def start
|
105
|
+
setup_connection unless @connection
|
106
|
+
connect
|
107
|
+
auth
|
108
|
+
send_presence
|
109
|
+
if @message_to_send_on_reconnect
|
110
|
+
send_message(@message_to_send_on_reconnect)
|
111
|
+
end
|
112
|
+
@message_to_send_on_reconnect = nil
|
113
|
+
end
|
114
|
+
|
115
|
+
#Stop the listener. (close the connection)
|
116
|
+
def stop
|
117
|
+
@connection.close if @connection and @connection.is_connected?
|
118
|
+
@connection = nil
|
119
|
+
end
|
120
|
+
|
121
|
+
def connect
|
122
|
+
@connection.connect
|
123
|
+
end
|
124
|
+
|
125
|
+
def auth
|
126
|
+
@connection.auth(@password)
|
127
|
+
end
|
128
|
+
|
129
|
+
def send_presence
|
130
|
+
presence_message = @config[:presence_message]
|
131
|
+
if presence_message && !presence_message.empty?
|
132
|
+
@connection.send(Jabber::Presence.new.set_show(:chat).set_status(presence_message))
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
#TODO: test and fix situation where we get disconnected while sending but then successfully reconnect
|
137
|
+
# (and make sure in such cases we resent)
|
138
|
+
def send_message(message)
|
139
|
+
unless @connection
|
140
|
+
raise ::ArgumentError, "Can't send messages while listener is stopped. Plase 'start' the listener first."
|
141
|
+
end
|
142
|
+
retry_count = 0
|
143
|
+
begin
|
144
|
+
while(not @connection.is_connected?)
|
145
|
+
#wait
|
146
|
+
Thread.pass
|
147
|
+
end
|
148
|
+
@message_now_sending = message
|
149
|
+
@connection.send(message)
|
150
|
+
return true #true, message was sent
|
151
|
+
rescue => e
|
152
|
+
if e.is_a?(Interrupt)
|
153
|
+
raise e
|
154
|
+
end
|
155
|
+
if(retry_count > @max_retry.to_i)
|
156
|
+
Jabber::debuglog "reached max retry count on message re-send, failing"
|
157
|
+
raise e
|
158
|
+
end
|
159
|
+
retry_count += 1
|
160
|
+
Jabber::debuglog "retrying message send.." + e.inspect
|
161
|
+
retry
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
end
|
data/lib/xmpp4r/rexmladdons.rb
CHANGED
@@ -17,6 +17,12 @@ module REXML
|
|
17
17
|
# this class adds a few helper methods to REXML::Element
|
18
18
|
class Element
|
19
19
|
|
20
|
+
def each_elements(*els, &block)
|
21
|
+
els.inject([ ]) do |res, e|
|
22
|
+
res + each_element(e, &block)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
20
26
|
##
|
21
27
|
# Replaces or adds a child element of name <tt>e</tt> with text <tt>t</tt>.
|
22
28
|
def replace_element_text(e, t)
|
@@ -36,7 +36,7 @@ module Jabber
|
|
36
36
|
# <b>Attention:</b> If you send presence and receive presences
|
37
37
|
# before the roster has arrived, the Roster helper will let them
|
38
38
|
# pass through and does *not* keep them!
|
39
|
-
def initialize(stream)
|
39
|
+
def initialize(stream, startnow = true)
|
40
40
|
@stream = stream
|
41
41
|
@items = {}
|
42
42
|
@items_lock = Mutex.new
|
@@ -66,10 +66,13 @@ module Jabber
|
|
66
66
|
handle_presence(pres)
|
67
67
|
end
|
68
68
|
}
|
69
|
+
get_roster if startnow
|
70
|
+
end
|
69
71
|
|
72
|
+
def get_roster
|
70
73
|
# Request the roster
|
71
74
|
rosterget = Iq.new_rosterget
|
72
|
-
stream.send(rosterget)
|
75
|
+
@stream.send(rosterget)
|
73
76
|
end
|
74
77
|
|
75
78
|
##
|
data/lib/xmpp4r/sasl.rb
CHANGED
@@ -122,7 +122,6 @@ module Jabber
|
|
122
122
|
state = :key
|
123
123
|
key = ''
|
124
124
|
value = ''
|
125
|
-
|
126
125
|
text.scan(/./) do |ch|
|
127
126
|
if state == :key
|
128
127
|
if ch == '='
|
@@ -133,6 +132,9 @@ module Jabber
|
|
133
132
|
|
134
133
|
elsif state == :value
|
135
134
|
if ch == ','
|
135
|
+
# due to our home-made parsing of the challenge, the key could have
|
136
|
+
# leading whitespace. strip it, or that would break jabberd2 support.
|
137
|
+
key = key.strip
|
136
138
|
res[key] = value
|
137
139
|
key = ''
|
138
140
|
value = ''
|
@@ -151,9 +153,12 @@ module Jabber
|
|
151
153
|
end
|
152
154
|
end
|
153
155
|
end
|
156
|
+
# due to our home-made parsing of the challenge, the key could have
|
157
|
+
# leading whitespace. strip it, or that would break jabberd2 support.
|
158
|
+
key = key.strip
|
154
159
|
res[key] = value unless key == ''
|
155
160
|
|
156
|
-
Jabber::debuglog("SASL DIGEST-MD5 challenge:\n#{text
|
161
|
+
Jabber::debuglog("SASL DIGEST-MD5 challenge:\n#{text}\n#{res.inspect}")
|
157
162
|
|
158
163
|
res
|
159
164
|
end
|
@@ -172,7 +177,7 @@ module Jabber
|
|
172
177
|
response['nc'] = '00000001'
|
173
178
|
response['qop'] = 'auth'
|
174
179
|
response['digest-uri'] = "xmpp/#{@stream.jid.domain}"
|
175
|
-
response['response'] = response_value(@stream.jid.node, @stream.jid.domain, response['digest-uri'], password, @nonce, response['cnonce'], response['qop'])
|
180
|
+
response['response'] = response_value(@stream.jid.node, @stream.jid.domain, response['digest-uri'], password, @nonce, response['cnonce'], response['qop'], response['authzid'])
|
176
181
|
response.each { |key,value|
|
177
182
|
unless %w(nc qop response charset).include? key
|
178
183
|
response[key] = "\"#{value}\""
|
@@ -180,7 +185,7 @@ module Jabber
|
|
180
185
|
}
|
181
186
|
|
182
187
|
response_text = response.collect { |k,v| "#{k}=#{v}" }.join(',')
|
183
|
-
Jabber::debuglog("SASL DIGEST-MD5 response:\n#{response_text}")
|
188
|
+
Jabber::debuglog("SASL DIGEST-MD5 response:\n#{response_text}\n#{response.inspect}")
|
184
189
|
|
185
190
|
r = REXML::Element.new('response')
|
186
191
|
r.add_namespace NS_SASL
|
@@ -224,14 +229,20 @@ module Jabber
|
|
224
229
|
|
225
230
|
##
|
226
231
|
# Calculate the value for the response field
|
227
|
-
def response_value(username, realm, digest_uri, passwd, nonce, cnonce, qop)
|
232
|
+
def response_value(username, realm, digest_uri, passwd, nonce, cnonce, qop, authzid)
|
228
233
|
a1_h = h("#{username}:#{realm}:#{passwd}")
|
229
234
|
a1 = "#{a1_h}:#{nonce}:#{cnonce}"
|
230
|
-
|
231
|
-
|
232
|
-
|
235
|
+
if authzid
|
236
|
+
a1 += ":#{authzid}"
|
237
|
+
end
|
238
|
+
if qop == 'auth-int' || qop == 'auth-conf'
|
239
|
+
a2 = "AUTHENTICATE:#{digest_uri}:00000000000000000000000000000000"
|
240
|
+
else
|
241
|
+
a2 = "AUTHENTICATE:#{digest_uri}"
|
242
|
+
end
|
233
243
|
hh("#{hh(a1)}:#{nonce}:00000001:#{cnonce}:#{qop}:#{hh(a2)}")
|
234
244
|
end
|
235
245
|
end
|
236
246
|
end
|
237
247
|
end
|
248
|
+
|
data/lib/xmpp4r/stream.rb
CHANGED
@@ -35,6 +35,9 @@ module Jabber
|
|
35
35
|
# connection status
|
36
36
|
attr_reader :status
|
37
37
|
|
38
|
+
# number of stanzas currently being processed
|
39
|
+
attr_reader :processing
|
40
|
+
|
38
41
|
##
|
39
42
|
# Initialize a new stream
|
40
43
|
def initialize
|
@@ -48,12 +51,14 @@ module Jabber
|
|
48
51
|
@send_lock = Mutex.new
|
49
52
|
@last_send = Time.now
|
50
53
|
@exception_block = nil
|
54
|
+
@tbcbmutex = Mutex.new
|
51
55
|
@threadblocks = []
|
52
56
|
@wakeup_thread = nil
|
53
57
|
@streamid = nil
|
54
58
|
@streamns = 'jabber:client'
|
55
59
|
@features_sem = Semaphore.new
|
56
60
|
@parser_thread = nil
|
61
|
+
@processing = 0
|
57
62
|
end
|
58
63
|
|
59
64
|
##
|
@@ -76,7 +81,7 @@ module Jabber
|
|
76
81
|
close!
|
77
82
|
end
|
78
83
|
rescue Exception => e
|
79
|
-
Jabber::
|
84
|
+
Jabber::warnlog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
|
80
85
|
|
81
86
|
if @exception_block
|
82
87
|
Thread.new do
|
@@ -85,7 +90,7 @@ module Jabber
|
|
85
90
|
@exception_block.call(e, self, :start)
|
86
91
|
end
|
87
92
|
else
|
88
|
-
Jabber::
|
93
|
+
Jabber::warnlog "Exception caught in Parser thread! (#{e.class})\n#{e.backtrace.join("\n")}"
|
89
94
|
close!
|
90
95
|
raise
|
91
96
|
end
|
@@ -116,7 +121,7 @@ module Jabber
|
|
116
121
|
##
|
117
122
|
# This method is called by the parser when a failure occurs
|
118
123
|
def parse_failure(e)
|
119
|
-
Jabber::
|
124
|
+
Jabber::warnlog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
|
120
125
|
|
121
126
|
# A new thread has to be created because close will cause the thread
|
122
127
|
# to commit suicide(???)
|
@@ -128,7 +133,7 @@ module Jabber
|
|
128
133
|
@exception_block.call(e, self, :parser)
|
129
134
|
end
|
130
135
|
else
|
131
|
-
Jabber::
|
136
|
+
Jabber::warnlog "Stream#parse_failure was called by XML parser. Dumping " +
|
132
137
|
"backtrace...\n" + e.exception + "\n#{e.backtrace.join("\n")}"
|
133
138
|
close
|
134
139
|
raise
|
@@ -170,6 +175,7 @@ module Jabber
|
|
170
175
|
#
|
171
176
|
# element:: [REXML::Element] The received element
|
172
177
|
def receive(element)
|
178
|
+
@tbcbmutex.synchronize { @processing += 1 }
|
173
179
|
Jabber::debuglog("RECEIVED:\n#{element.to_s}")
|
174
180
|
|
175
181
|
if element.namespace('').to_s == '' # REXML namespaces are always strings
|
@@ -219,13 +225,21 @@ module Jabber
|
|
219
225
|
end
|
220
226
|
end
|
221
227
|
|
228
|
+
if @xmlcbs.process(stanza)
|
229
|
+
@tbcbmutex.synchronize { @processing -= 1 }
|
230
|
+
return true
|
231
|
+
end
|
232
|
+
|
222
233
|
# Iterate through blocked threads (= waiting for an answer)
|
223
234
|
#
|
224
235
|
# We're dup'ping the @threadblocks here, so that we won't end up in an
|
225
236
|
# endless loop if Stream#send is being nested. That means, the nested
|
226
237
|
# threadblock won't receive the stanza currently processed, but the next
|
227
238
|
# one.
|
228
|
-
threadblocks =
|
239
|
+
threadblocks = nil
|
240
|
+
@tbcbmutex.synchronize do
|
241
|
+
threadblocks = @threadblocks.dup
|
242
|
+
end
|
229
243
|
threadblocks.each { |threadblock|
|
230
244
|
exception = nil
|
231
245
|
r = false
|
@@ -236,26 +250,68 @@ module Jabber
|
|
236
250
|
end
|
237
251
|
|
238
252
|
if r == true
|
239
|
-
@
|
253
|
+
@tbcbmutex.synchronize do
|
254
|
+
@threadblocks.delete(threadblock)
|
255
|
+
end
|
240
256
|
threadblock.wakeup
|
241
|
-
|
257
|
+
@tbcbmutex.synchronize { @processing -= 1 }
|
258
|
+
return true
|
242
259
|
elsif exception
|
243
|
-
@
|
260
|
+
@tbcbmutex.synchronize do
|
261
|
+
@threadblocks.delete(threadblock)
|
262
|
+
end
|
244
263
|
threadblock.raise(exception)
|
245
264
|
end
|
246
265
|
}
|
247
266
|
|
248
267
|
Jabber::debuglog("PROCESSING:\n#{stanza.to_s} (#{stanza.class})")
|
249
|
-
|
250
|
-
|
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...")
|
251
275
|
case stanza
|
252
276
|
when Message
|
253
|
-
|
277
|
+
r = @messagecbs.process(stanza)
|
254
278
|
when Iq
|
255
|
-
|
279
|
+
r = @iqcbs.process(stanza)
|
256
280
|
when Presence
|
257
|
-
|
281
|
+
r = @presencecbs.process(stanza)
|
258
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
|
259
315
|
end
|
260
316
|
|
261
317
|
##
|
@@ -276,7 +332,6 @@ module Jabber
|
|
276
332
|
raise @exception if @exception
|
277
333
|
end
|
278
334
|
def wakeup
|
279
|
-
# TODO: Handle threadblock removal if !alive?
|
280
335
|
@waiter.run
|
281
336
|
end
|
282
337
|
def raise(exception)
|
@@ -305,11 +360,17 @@ module Jabber
|
|
305
360
|
# &block:: [Block] The optional block
|
306
361
|
def send(xml, &block)
|
307
362
|
Jabber::debuglog("SENDING:\n#{xml}")
|
308
|
-
|
363
|
+
if block
|
364
|
+
threadblock = ThreadBlock.new(block)
|
365
|
+
@tbcbmutex.synchronize do
|
366
|
+
@threadblocks.unshift(threadblock)
|
367
|
+
end
|
368
|
+
end
|
309
369
|
begin
|
310
370
|
# Temporarily remove stanza's namespace to
|
311
371
|
# reduce bandwidth consumption
|
312
|
-
if xml.kind_of? XMPPStanza and xml.namespace == 'jabber:client'
|
372
|
+
if xml.kind_of? XMPPStanza and xml.namespace == 'jabber:client' and
|
373
|
+
xml.prefix != 'stream' and xml.name != 'stream'
|
313
374
|
xml.delete_namespace
|
314
375
|
send_data(xml.to_s)
|
315
376
|
xml.add_namespace(@streamns)
|
@@ -317,7 +378,7 @@ module Jabber
|
|
317
378
|
send_data(xml.to_s)
|
318
379
|
end
|
319
380
|
rescue Exception => e
|
320
|
-
Jabber::
|
381
|
+
Jabber::warnlog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
|
321
382
|
|
322
383
|
if @exception_block
|
323
384
|
Thread.new do
|
@@ -326,7 +387,7 @@ module Jabber
|
|
326
387
|
@exception_block.call(e, self, :sending)
|
327
388
|
end
|
328
389
|
else
|
329
|
-
Jabber::
|
390
|
+
Jabber::warnlog "Exception caught while sending! (#{e.class})\n#{e.backtrace.join("\n")}"
|
330
391
|
close!
|
331
392
|
raise
|
332
393
|
end
|
@@ -336,7 +397,7 @@ module Jabber
|
|
336
397
|
if block and Thread.current != @parser_thread
|
337
398
|
threadblock.wait
|
338
399
|
elsif block
|
339
|
-
Jabber::
|
400
|
+
Jabber::warnlog("WARNING:\nCannot stop current thread in Jabber::Stream#send because it is the parser thread!")
|
340
401
|
end
|
341
402
|
end
|
342
403
|
|
@@ -393,13 +454,17 @@ module Jabber
|
|
393
454
|
end
|
394
455
|
|
395
456
|
##
|
396
|
-
# Adds a callback block to process received XML messages
|
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.
|
397
460
|
#
|
398
461
|
# priority:: [Integer] The callback's priority, the higher, the sooner
|
399
462
|
# ref:: [String] The callback's reference
|
400
463
|
# &block:: [Block] The optional block
|
401
464
|
def add_xml_callback(priority = 0, ref = nil, &block)
|
402
|
-
@
|
465
|
+
@tbcbmutex.synchronize do
|
466
|
+
@xmlcbs.add(priority, ref, block)
|
467
|
+
end
|
403
468
|
end
|
404
469
|
|
405
470
|
##
|
@@ -407,7 +472,9 @@ module Jabber
|
|
407
472
|
#
|
408
473
|
# ref:: [String] The reference of the callback to delete
|
409
474
|
def delete_xml_callback(ref)
|
410
|
-
@
|
475
|
+
@tbcbmutex.synchronize do
|
476
|
+
@xmlcbs.delete(ref)
|
477
|
+
end
|
411
478
|
end
|
412
479
|
|
413
480
|
##
|
@@ -417,7 +484,9 @@ module Jabber
|
|
417
484
|
# ref:: [String] The callback's reference
|
418
485
|
# &block:: [Block] The optional block
|
419
486
|
def add_message_callback(priority = 0, ref = nil, &block)
|
420
|
-
@
|
487
|
+
@tbcbmutex.synchronize do
|
488
|
+
@messagecbs.add(priority, ref, block)
|
489
|
+
end
|
421
490
|
end
|
422
491
|
|
423
492
|
##
|
@@ -425,7 +494,9 @@ module Jabber
|
|
425
494
|
#
|
426
495
|
# ref:: [String] The reference of the callback to delete
|
427
496
|
def delete_message_callback(ref)
|
428
|
-
@
|
497
|
+
@tbcbmutex.synchronize do
|
498
|
+
@messagecbs.delete(ref)
|
499
|
+
end
|
429
500
|
end
|
430
501
|
|
431
502
|
##
|
@@ -435,7 +506,9 @@ module Jabber
|
|
435
506
|
# ref:: [String] The callback's reference
|
436
507
|
# &block:: [Block] The optional block
|
437
508
|
def add_stanza_callback(priority = 0, ref = nil, &block)
|
438
|
-
@
|
509
|
+
@tbcbmutex.synchronize do
|
510
|
+
@stanzacbs.add(priority, ref, block)
|
511
|
+
end
|
439
512
|
end
|
440
513
|
|
441
514
|
##
|
@@ -443,7 +516,9 @@ module Jabber
|
|
443
516
|
#
|
444
517
|
# ref:: [String] The reference of the callback to delete
|
445
518
|
def delete_stanza_callback(ref)
|
446
|
-
@
|
519
|
+
@tbcbmutex.synchronize do
|
520
|
+
@stanzacbs.delete(ref)
|
521
|
+
end
|
447
522
|
end
|
448
523
|
|
449
524
|
##
|
@@ -453,7 +528,9 @@ module Jabber
|
|
453
528
|
# ref:: [String] The callback's reference
|
454
529
|
# &block:: [Block] The optional block
|
455
530
|
def add_presence_callback(priority = 0, ref = nil, &block)
|
456
|
-
@
|
531
|
+
@tbcbmutex.synchronize do
|
532
|
+
@presencecbs.add(priority, ref, block)
|
533
|
+
end
|
457
534
|
end
|
458
535
|
|
459
536
|
##
|
@@ -461,7 +538,9 @@ module Jabber
|
|
461
538
|
#
|
462
539
|
# ref:: [String] The reference of the callback to delete
|
463
540
|
def delete_presence_callback(ref)
|
464
|
-
@
|
541
|
+
@tbcbmutex.synchronize do
|
542
|
+
@presencecbs.delete(ref)
|
543
|
+
end
|
465
544
|
end
|
466
545
|
|
467
546
|
##
|
@@ -471,7 +550,9 @@ module Jabber
|
|
471
550
|
# ref:: [String] The callback's reference
|
472
551
|
# &block:: [Block] The optional block
|
473
552
|
def add_iq_callback(priority = 0, ref = nil, &block)
|
474
|
-
@
|
553
|
+
@tbcbmutex.synchronize do
|
554
|
+
@iqcbs.add(priority, ref, block)
|
555
|
+
end
|
475
556
|
end
|
476
557
|
|
477
558
|
##
|
@@ -480,7 +561,9 @@ module Jabber
|
|
480
561
|
# ref:: [String] The reference of the callback to delete
|
481
562
|
#
|
482
563
|
def delete_iq_callback(ref)
|
483
|
-
@
|
564
|
+
@tbcbmutex.synchronize do
|
565
|
+
@iqcbs.delete(ref)
|
566
|
+
end
|
484
567
|
end
|
485
568
|
##
|
486
569
|
# Closes the connection to the Jabber service
|
@@ -489,9 +572,28 @@ module Jabber
|
|
489
572
|
end
|
490
573
|
|
491
574
|
def close!
|
492
|
-
|
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
|
493
594
|
@fd.close if @fd and !@fd.closed?
|
494
595
|
@status = DISCONNECTED
|
596
|
+
@parser_thread.kill if @parser_thread
|
495
597
|
end
|
496
598
|
end
|
497
599
|
end
|