xmpp4r 0.4 → 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.
- 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
|