shingara-blather 0.4.8
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/LICENSE +22 -0
- data/README.md +162 -0
- data/examples/echo.rb +18 -0
- data/examples/execute.rb +16 -0
- data/examples/ping_pong.rb +37 -0
- data/examples/print_hierarchy.rb +76 -0
- data/examples/rosterprint.rb +14 -0
- data/examples/stream_only.rb +27 -0
- data/examples/xmpp4r/echo.rb +35 -0
- data/lib/blather/client/client.rb +310 -0
- data/lib/blather/client/dsl/pubsub.rb +170 -0
- data/lib/blather/client/dsl.rb +264 -0
- data/lib/blather/client.rb +87 -0
- data/lib/blather/core_ext/nokogiri.rb +40 -0
- data/lib/blather/errors/sasl_error.rb +43 -0
- data/lib/blather/errors/stanza_error.rb +107 -0
- data/lib/blather/errors/stream_error.rb +82 -0
- data/lib/blather/errors.rb +69 -0
- data/lib/blather/jid.rb +142 -0
- data/lib/blather/roster.rb +111 -0
- data/lib/blather/roster_item.rb +122 -0
- data/lib/blather/stanza/disco/disco_info.rb +176 -0
- data/lib/blather/stanza/disco/disco_items.rb +132 -0
- data/lib/blather/stanza/disco.rb +25 -0
- data/lib/blather/stanza/iq/query.rb +53 -0
- data/lib/blather/stanza/iq/roster.rb +179 -0
- data/lib/blather/stanza/iq.rb +138 -0
- data/lib/blather/stanza/message.rb +332 -0
- data/lib/blather/stanza/presence/status.rb +212 -0
- data/lib/blather/stanza/presence/subscription.rb +101 -0
- data/lib/blather/stanza/presence.rb +163 -0
- data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
- data/lib/blather/stanza/pubsub/create.rb +65 -0
- data/lib/blather/stanza/pubsub/errors.rb +18 -0
- data/lib/blather/stanza/pubsub/event.rb +123 -0
- data/lib/blather/stanza/pubsub/items.rb +103 -0
- data/lib/blather/stanza/pubsub/publish.rb +103 -0
- data/lib/blather/stanza/pubsub/retract.rb +92 -0
- data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
- data/lib/blather/stanza/pubsub/subscription.rb +134 -0
- data/lib/blather/stanza/pubsub/subscriptions.rb +81 -0
- data/lib/blather/stanza/pubsub/unsubscribe.rb +68 -0
- data/lib/blather/stanza/pubsub.rb +129 -0
- data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
- data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
- data/lib/blather/stanza/pubsub_owner.rb +51 -0
- data/lib/blather/stanza.rb +149 -0
- data/lib/blather/stream/client.rb +31 -0
- data/lib/blather/stream/component.rb +38 -0
- data/lib/blather/stream/features/resource.rb +63 -0
- data/lib/blather/stream/features/sasl.rb +187 -0
- data/lib/blather/stream/features/session.rb +44 -0
- data/lib/blather/stream/features/tls.rb +28 -0
- data/lib/blather/stream/features.rb +53 -0
- data/lib/blather/stream/parser.rb +102 -0
- data/lib/blather/stream.rb +231 -0
- data/lib/blather/xmpp_node.rb +218 -0
- data/lib/blather.rb +78 -0
- data/spec/blather/client/client_spec.rb +559 -0
- data/spec/blather/client/dsl/pubsub_spec.rb +462 -0
- data/spec/blather/client/dsl_spec.rb +143 -0
- data/spec/blather/core_ext/nokogiri_spec.rb +83 -0
- data/spec/blather/errors/sasl_error_spec.rb +33 -0
- data/spec/blather/errors/stanza_error_spec.rb +129 -0
- data/spec/blather/errors/stream_error_spec.rb +108 -0
- data/spec/blather/errors_spec.rb +33 -0
- data/spec/blather/jid_spec.rb +87 -0
- data/spec/blather/roster_item_spec.rb +96 -0
- data/spec/blather/roster_spec.rb +103 -0
- data/spec/blather/stanza/discos/disco_info_spec.rb +226 -0
- data/spec/blather/stanza/discos/disco_items_spec.rb +148 -0
- data/spec/blather/stanza/iq/query_spec.rb +64 -0
- data/spec/blather/stanza/iq/roster_spec.rb +140 -0
- data/spec/blather/stanza/iq_spec.rb +45 -0
- data/spec/blather/stanza/message_spec.rb +132 -0
- data/spec/blather/stanza/presence/status_spec.rb +132 -0
- data/spec/blather/stanza/presence/subscription_spec.rb +105 -0
- data/spec/blather/stanza/presence_spec.rb +66 -0
- data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
- data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
- data/spec/blather/stanza/pubsub/event_spec.rb +84 -0
- data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
- data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
- data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
- data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
- data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
- data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
- data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +61 -0
- data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
- data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
- data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
- data/spec/blather/stanza/pubsub_spec.rb +67 -0
- data/spec/blather/stanza_spec.rb +116 -0
- data/spec/blather/stream/client_spec.rb +1011 -0
- data/spec/blather/stream/component_spec.rb +95 -0
- data/spec/blather/stream/parser_spec.rb +145 -0
- data/spec/blather/xmpp_node_spec.rb +231 -0
- data/spec/fixtures/pubsub.rb +311 -0
- data/spec/spec_helper.rb +43 -0
- metadata +249 -0
|
@@ -0,0 +1,1011 @@
|
|
|
1
|
+
require 'resolv'
|
|
2
|
+
require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
|
|
3
|
+
|
|
4
|
+
describe Blather::Stream::Client do
|
|
5
|
+
class MockServer; end
|
|
6
|
+
module ServerMock
|
|
7
|
+
def receive_data(data)
|
|
8
|
+
@server ||= MockServer.new
|
|
9
|
+
@server.receive_data data, self
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def mocked_server(times = nil, &block)
|
|
14
|
+
@client ||= mock()
|
|
15
|
+
@client.stubs(:unbind) unless @client.respond_to?(:unbind)
|
|
16
|
+
@client.stubs(:post_init) unless @client.respond_to?(:post_init)
|
|
17
|
+
@client.stubs(:jid=) unless @client.respond_to?(:jid=)
|
|
18
|
+
|
|
19
|
+
MockServer.any_instance.expects(:receive_data).send(*(times ? [:times, times] : [:at_least, 1])).with &block
|
|
20
|
+
EventMachine::run {
|
|
21
|
+
EM.add_timer(0.5) { EM.stop if EM.reactor_running? }
|
|
22
|
+
|
|
23
|
+
# Mocked server
|
|
24
|
+
EventMachine::start_server '127.0.0.1', 12345, ServerMock
|
|
25
|
+
|
|
26
|
+
# Blather::Stream connection
|
|
27
|
+
EM.connect('127.0.0.1', 12345, Blather::Stream::Client, @client, @jid || Blather::JID.new('n@d/r'), 'pass') { |c| @stream = c }
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
it 'can be started' do
|
|
33
|
+
client = mock()
|
|
34
|
+
params = [client, 'n@d/r', 'pass', 'host', 1234]
|
|
35
|
+
EM.expects(:connect).with do |*parms|
|
|
36
|
+
parms[0].must_equal 'host'
|
|
37
|
+
parms[1].must_equal 1234
|
|
38
|
+
parms[3].must_equal client
|
|
39
|
+
parms[5].must_equal 'pass'
|
|
40
|
+
parms[4].must_equal Blather::JID.new('n@d/r')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Blather::Stream::Client.start *params
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'attempts to find the SRV record if a host is not provided' do
|
|
47
|
+
dns = mock(:sort! => nil, :empty? => false)
|
|
48
|
+
dns.expects(:each).yields(mock({
|
|
49
|
+
:target => 'd',
|
|
50
|
+
:port => 5222
|
|
51
|
+
}))
|
|
52
|
+
Resolv::DNS.expects(:open).yields(mock(:getresources => dns))
|
|
53
|
+
|
|
54
|
+
client = Class.new
|
|
55
|
+
EM.expects(:connect).with do |*parms|
|
|
56
|
+
parms[0].must_equal 'd'
|
|
57
|
+
parms[1].must_equal 5222
|
|
58
|
+
parms[3].must_equal client
|
|
59
|
+
parms[5].must_equal 'pass'
|
|
60
|
+
parms[4].must_equal Blather::JID.new('n@d/r')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
Blather::Stream::Client.start client, 'n@d/r', 'pass'
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'will attempt as many connections as it takes' do
|
|
67
|
+
dns = [mock(:target => 'd', :port => 5222), mock(:target => 'g', :port => 1234)]
|
|
68
|
+
dns.stubs(:sort!) #ignore sorting
|
|
69
|
+
Resolv::DNS.expects(:open).yields(mock(:getresources => dns))
|
|
70
|
+
|
|
71
|
+
client = Class.new
|
|
72
|
+
EM.expects(:connect).with do |*parms|
|
|
73
|
+
raise Blather::Stream::NoConnection if parms[0] == 'd'
|
|
74
|
+
parms[0].must_equal 'g'
|
|
75
|
+
parms[1].must_equal 1234
|
|
76
|
+
parms[3].must_equal client
|
|
77
|
+
parms[5].must_equal 'pass'
|
|
78
|
+
parms[4].must_equal Blather::JID.new('n@d/r')
|
|
79
|
+
end
|
|
80
|
+
Blather::Stream::Client.start client, 'n@d/r', 'pass'
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'will not attempt to connect more often than necessary' do
|
|
84
|
+
dns = [mock(:target => 'd', :port => 5222), mock()]
|
|
85
|
+
dns.stubs(:sort!) #ignore sorting
|
|
86
|
+
Resolv::DNS.expects(:open).yields(mock(:getresources => dns))
|
|
87
|
+
|
|
88
|
+
client = Class.new
|
|
89
|
+
EM.expects(:connect).with do |*parms|
|
|
90
|
+
parms[0].must_equal 'd'
|
|
91
|
+
parms[1].must_equal 5222
|
|
92
|
+
parms[3].must_equal client
|
|
93
|
+
parms[5].must_equal 'pass'
|
|
94
|
+
parms[4].must_equal Blather::JID.new('n@d/r')
|
|
95
|
+
end
|
|
96
|
+
Blather::Stream::Client.start client, 'n@d/r', 'pass'
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'can figure out the host to use based on the jid' do
|
|
100
|
+
Resolv::DNS.expects(:open).yields(mock(:getresources => mock(:empty? => true)))
|
|
101
|
+
client = Class.new
|
|
102
|
+
params = [client, 'n@d/r', 'pass', nil, 5222]
|
|
103
|
+
EM.expects(:connect).with do |*parms|
|
|
104
|
+
parms[0].must_equal 'd'
|
|
105
|
+
parms[1].must_equal 5222
|
|
106
|
+
parms[3].must_equal client
|
|
107
|
+
parms[5].must_equal 'pass'
|
|
108
|
+
parms[4].must_equal Blather::JID.new('n@d/r')
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
Blather::Stream::Client.start client, 'n@d/r', 'pass'
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'starts the stream once the connection is complete' do
|
|
115
|
+
mocked_server(1) { |val, _| EM.stop; val.must_match(/stream:stream/) }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'sends stanzas to the client when the stream is ready' do
|
|
119
|
+
@client = mock()
|
|
120
|
+
@client.expects(:receive_data).with do |n|
|
|
121
|
+
EM.stop
|
|
122
|
+
n.must_be_kind_of Blather::Stanza::Message
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
mocked_server(1) do |val, server|
|
|
126
|
+
val.must_match(/stream:stream/)
|
|
127
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
128
|
+
server.send_data "<message to='a@b/c' from='d@e/f' type='chat' xml:lang='en'><body>Message!</body></message>"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it 'puts itself in the stopped state and calls @client.unbind when unbound' do
|
|
133
|
+
@client = mock()
|
|
134
|
+
@client.expects(:unbind).at_least_once
|
|
135
|
+
|
|
136
|
+
started = false
|
|
137
|
+
mocked_server(2) do |val, server|
|
|
138
|
+
if !started
|
|
139
|
+
started = true
|
|
140
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
141
|
+
server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
|
|
142
|
+
val.must_match(/stream:stream/)
|
|
143
|
+
|
|
144
|
+
else
|
|
145
|
+
EM.stop
|
|
146
|
+
@stream.stopped?.must_equal false
|
|
147
|
+
@stream.unbind
|
|
148
|
+
@stream.stopped?.must_equal true
|
|
149
|
+
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it 'will be in the negotiating state during feature negotiations' do
|
|
155
|
+
state = nil
|
|
156
|
+
@client = mock()
|
|
157
|
+
@client.expects(:receive_data).with do |n|
|
|
158
|
+
EM.stop
|
|
159
|
+
state.must_equal(:negotiated) && @stream.negotiating?.must_equal(false)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
mocked_server(2) do |val, server|
|
|
163
|
+
case state
|
|
164
|
+
when nil
|
|
165
|
+
state = :started
|
|
166
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
167
|
+
server.send_data "<stream:features><session xmlns='urn:ietf:params:xml:ns:xmpp-session' /></stream:features>"
|
|
168
|
+
true
|
|
169
|
+
|
|
170
|
+
when :started
|
|
171
|
+
state = :negotiated
|
|
172
|
+
@stream.negotiating?.must_equal true
|
|
173
|
+
server.send_data "<iq from='d' type='result' id='#{val[/id="([^"]+)"/,1]}' />"
|
|
174
|
+
server.send_data "<message to='a@b/c' from='d@e/f' type='chat' xml:lang='en'><body>Message!</body></message>"
|
|
175
|
+
true
|
|
176
|
+
|
|
177
|
+
else
|
|
178
|
+
EM.stop
|
|
179
|
+
false
|
|
180
|
+
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it 'stops when sent </stream:stream>' do
|
|
186
|
+
state = nil
|
|
187
|
+
mocked_server(3) do |val, server|
|
|
188
|
+
case state
|
|
189
|
+
when nil
|
|
190
|
+
state = :started
|
|
191
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xml:lang='en'>"
|
|
192
|
+
server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
|
|
193
|
+
val.must_match(/stream:stream/)
|
|
194
|
+
|
|
195
|
+
when :started
|
|
196
|
+
state = :stopped
|
|
197
|
+
server.send_data '</stream:stream>'
|
|
198
|
+
@stream.stopped?.must_equal false
|
|
199
|
+
|
|
200
|
+
when :stopped
|
|
201
|
+
EM.stop
|
|
202
|
+
@stream.stopped?.must_equal true
|
|
203
|
+
val.must_equal '</stream:stream>'
|
|
204
|
+
|
|
205
|
+
else
|
|
206
|
+
EM.stop
|
|
207
|
+
false
|
|
208
|
+
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
it 'sends client an error on stream:error' do
|
|
214
|
+
@client = mock()
|
|
215
|
+
@client.expects(:receive_data).with do |v|
|
|
216
|
+
v.name.must_equal :conflict
|
|
217
|
+
v.text.must_equal 'Already signed in'
|
|
218
|
+
v.to_s.must_equal "Stream Error (conflict): #{v.text}"
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
state = nil
|
|
222
|
+
mocked_server(3) do |val, server|
|
|
223
|
+
case state
|
|
224
|
+
when nil
|
|
225
|
+
state = :started
|
|
226
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"
|
|
227
|
+
server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
|
|
228
|
+
val.must_match(/stream:stream/)
|
|
229
|
+
|
|
230
|
+
when :started
|
|
231
|
+
state = :stopped
|
|
232
|
+
server.send_data "<stream:error><conflict xmlns='urn:ietf:params:xml:ns:xmpp-streams' />"
|
|
233
|
+
server.send_data "<text xmlns='urn:ietf:params:xml:ns:xmpp-streams'>Already signed in</text></stream:error>"
|
|
234
|
+
|
|
235
|
+
when :stopped
|
|
236
|
+
EM.stop
|
|
237
|
+
val.must_equal "</stream:stream>"
|
|
238
|
+
|
|
239
|
+
else
|
|
240
|
+
EM.stop
|
|
241
|
+
false
|
|
242
|
+
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it 'skips features it is unable to handle' do
|
|
248
|
+
state = nil
|
|
249
|
+
mocked_server() do |val, server|
|
|
250
|
+
case state
|
|
251
|
+
when nil
|
|
252
|
+
state = :started
|
|
253
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
254
|
+
server.send_data "<stream:features><auth xmlns='http://jabber.org/features/iq-auth'/><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' /></stream:features>"
|
|
255
|
+
val.must_match(/stream:stream/)
|
|
256
|
+
|
|
257
|
+
when :started
|
|
258
|
+
EM.stop
|
|
259
|
+
val.must_match(/starttls/)
|
|
260
|
+
|
|
261
|
+
else
|
|
262
|
+
EM.stop
|
|
263
|
+
false
|
|
264
|
+
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
it 'starts TLS when asked' do
|
|
270
|
+
state = nil
|
|
271
|
+
mocked_server(3) do |val, server|
|
|
272
|
+
case state
|
|
273
|
+
when nil
|
|
274
|
+
state = :started
|
|
275
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
276
|
+
server.send_data "<stream:features><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' /></stream:features>"
|
|
277
|
+
val.must_match(/stream:stream/)
|
|
278
|
+
|
|
279
|
+
when :started
|
|
280
|
+
state = :tls
|
|
281
|
+
@stream.expects(:start_tls)
|
|
282
|
+
server.send_data "<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
|
|
283
|
+
val.must_match(/starttls/)
|
|
284
|
+
|
|
285
|
+
when :tls
|
|
286
|
+
EM.stop
|
|
287
|
+
true
|
|
288
|
+
|
|
289
|
+
else
|
|
290
|
+
EM.stop
|
|
291
|
+
false
|
|
292
|
+
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
it 'will fail if TLS negotiation fails' do
|
|
298
|
+
state = nil
|
|
299
|
+
@client = mock()
|
|
300
|
+
@client.expects(:receive_data).with { |v| v.must_be_kind_of Blather::Stream::TLS::TLSFailure }
|
|
301
|
+
mocked_server(3) do |val, server|
|
|
302
|
+
case state
|
|
303
|
+
when nil
|
|
304
|
+
state = :started
|
|
305
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'><stream:features><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' /></stream:features>"
|
|
306
|
+
val.must_match(/stream:stream/)
|
|
307
|
+
|
|
308
|
+
when :started
|
|
309
|
+
state = :tls
|
|
310
|
+
@stream.expects(:start_tls).never
|
|
311
|
+
server.send_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/></stream:stream>"
|
|
312
|
+
val.must_match(/starttls/)
|
|
313
|
+
|
|
314
|
+
when :tls
|
|
315
|
+
EM.stop
|
|
316
|
+
val.must_equal "</stream:stream>"
|
|
317
|
+
|
|
318
|
+
else
|
|
319
|
+
EM.stop
|
|
320
|
+
false
|
|
321
|
+
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
it 'will fail if a bad node comes through TLS negotiations' do
|
|
327
|
+
state = nil
|
|
328
|
+
@client = mock()
|
|
329
|
+
@client.expects(:receive_data).with do |v|
|
|
330
|
+
v.must_be_kind_of Blather::Stream::TLS::TLSFailure
|
|
331
|
+
end
|
|
332
|
+
mocked_server(3) do |val, server|
|
|
333
|
+
case state
|
|
334
|
+
when nil
|
|
335
|
+
state = :started
|
|
336
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
337
|
+
server.send_data "<stream:features><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' /></stream:features>"
|
|
338
|
+
val.must_match(/stream:stream/)
|
|
339
|
+
|
|
340
|
+
when :started
|
|
341
|
+
state = :tls
|
|
342
|
+
@stream.expects(:start_tls).never
|
|
343
|
+
server.send_data "<foo-bar xmlns='urn:ietf:params:xml:ns:xmpp-tls'/></stream:stream>"
|
|
344
|
+
val.must_match(/starttls/)
|
|
345
|
+
|
|
346
|
+
when :tls
|
|
347
|
+
EM.stop
|
|
348
|
+
val.must_equal "</stream:stream>"
|
|
349
|
+
|
|
350
|
+
else
|
|
351
|
+
EM.stop
|
|
352
|
+
false
|
|
353
|
+
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
it 'connects via SASL MD5 when asked' do
|
|
359
|
+
Time.any_instance.stubs(:to_f).returns(1.1)
|
|
360
|
+
state = nil
|
|
361
|
+
|
|
362
|
+
mocked_server(5) do |val, server|
|
|
363
|
+
case state
|
|
364
|
+
when nil
|
|
365
|
+
state = :started
|
|
366
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'><stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism></mechanisms></stream:features>"
|
|
367
|
+
val.must_match(/stream:stream/)
|
|
368
|
+
|
|
369
|
+
when :started
|
|
370
|
+
state = :auth_sent
|
|
371
|
+
server.send_data "<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNzCg==</challenge>"
|
|
372
|
+
val.must_match(/auth.*DIGEST\-MD5/)
|
|
373
|
+
|
|
374
|
+
when :auth_sent
|
|
375
|
+
state = :response1_sent
|
|
376
|
+
server.send_data "<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZAo=</challenge>"
|
|
377
|
+
val.must_equal('<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">bm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixjaGFyc2V0PXV0Zi04LHVzZXJuYW1lPSJuIixyZWFsbT0ic29tZXJlYWxtIixjbm9uY2U9Ijc3N2Q0NWJiYmNkZjUwZDQ5YzQyYzcwYWQ3YWNmNWZlIixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJ4bXBwL2QiLHJlc3BvbnNlPTZiNTlhY2Q1ZWJmZjhjZTA0NTYzMGFiMDU2Zjg3MTdm</response>')
|
|
378
|
+
|
|
379
|
+
when :response1_sent
|
|
380
|
+
state = :response2_sent
|
|
381
|
+
server.send_data "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />"
|
|
382
|
+
val.must_match(%r{<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl"\s?/>})
|
|
383
|
+
|
|
384
|
+
when :response2_sent
|
|
385
|
+
EM.stop
|
|
386
|
+
state = :complete
|
|
387
|
+
val.must_match(/stream:stream/)
|
|
388
|
+
|
|
389
|
+
else
|
|
390
|
+
EM.stop
|
|
391
|
+
false
|
|
392
|
+
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
it 'will connect via SSL PLAIN when asked' do
|
|
398
|
+
state = nil
|
|
399
|
+
mocked_server(3) do |val, server|
|
|
400
|
+
case state
|
|
401
|
+
when nil
|
|
402
|
+
state = :started
|
|
403
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'><stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>PLAIN</mechanism></mechanisms></stream:features>"
|
|
404
|
+
val.must_match(/stream:stream/)
|
|
405
|
+
|
|
406
|
+
when :started
|
|
407
|
+
state = :auth_sent
|
|
408
|
+
server.send_data "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />"
|
|
409
|
+
val.must_equal('<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">bkBkAG4AcGFzcw==</auth>')
|
|
410
|
+
|
|
411
|
+
when :auth_sent
|
|
412
|
+
EM.stop
|
|
413
|
+
state = :complete
|
|
414
|
+
val.must_match(/stream:stream/)
|
|
415
|
+
|
|
416
|
+
else
|
|
417
|
+
EM.stop
|
|
418
|
+
false
|
|
419
|
+
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
it 'will connect via SSL ANONYMOUS when asked' do
|
|
425
|
+
state = nil
|
|
426
|
+
|
|
427
|
+
mocked_server(3) do |val, server|
|
|
428
|
+
case state
|
|
429
|
+
when nil
|
|
430
|
+
state = :started
|
|
431
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'><stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>ANONYMOUS</mechanism></mechanisms></stream:features>"
|
|
432
|
+
val.must_match(/stream:stream/)
|
|
433
|
+
|
|
434
|
+
when :started
|
|
435
|
+
state = :auth_sent
|
|
436
|
+
server.send_data "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />"
|
|
437
|
+
val.must_equal('<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="ANONYMOUS"/>')
|
|
438
|
+
|
|
439
|
+
when :auth_sent
|
|
440
|
+
EM.stop
|
|
441
|
+
state = :complete
|
|
442
|
+
val.must_match(/stream:stream/)
|
|
443
|
+
|
|
444
|
+
else
|
|
445
|
+
EM.stop
|
|
446
|
+
false
|
|
447
|
+
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
it 'connects via ANONYMOUS if the Blather::JID has a blank node' do
|
|
453
|
+
state = nil
|
|
454
|
+
@jid = Blather::JID.new '@d'
|
|
455
|
+
|
|
456
|
+
mocked_server(3) do |val, server|
|
|
457
|
+
case state
|
|
458
|
+
when nil
|
|
459
|
+
state = :started
|
|
460
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'><stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism><mechanism>PLAIN</mechanism><mechanism>ANONYMOUS</mechanism></mechanisms></stream:features>"
|
|
461
|
+
val.must_match(/stream:stream/)
|
|
462
|
+
|
|
463
|
+
when :started
|
|
464
|
+
state = :auth_sent
|
|
465
|
+
server.send_data "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />"
|
|
466
|
+
val.must_equal('<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="ANONYMOUS"/>')
|
|
467
|
+
|
|
468
|
+
when :auth_sent
|
|
469
|
+
EM.stop
|
|
470
|
+
state = :complete
|
|
471
|
+
val.must_match(/stream:stream/)
|
|
472
|
+
|
|
473
|
+
else
|
|
474
|
+
EM.stop
|
|
475
|
+
false
|
|
476
|
+
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
it 'fails if asked to connect via ANONYMOUS but the server does not support it' do
|
|
482
|
+
state = nil
|
|
483
|
+
@jid = Blather::JID.new '@d'
|
|
484
|
+
@client = mock()
|
|
485
|
+
@client.expects(:receive_data).with { |s| s.must_be_instance_of Blather::BlatherError }
|
|
486
|
+
|
|
487
|
+
mocked_server(2) do |val, server|
|
|
488
|
+
case state
|
|
489
|
+
when nil
|
|
490
|
+
state = :started
|
|
491
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
492
|
+
server.send_data "<stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism><mechanism>PLAIN</mechanism></mechanisms></stream:features>"
|
|
493
|
+
val.must_match(/stream:stream/)
|
|
494
|
+
|
|
495
|
+
when :started
|
|
496
|
+
EM.stop
|
|
497
|
+
val.must_match(/stream:stream/)
|
|
498
|
+
|
|
499
|
+
else
|
|
500
|
+
EM.stop
|
|
501
|
+
false
|
|
502
|
+
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
it 'tries each possible mechanism until it fails completely' do
|
|
508
|
+
state = nil
|
|
509
|
+
@client = mock()
|
|
510
|
+
@client.expects(:receive_data).with do |n|
|
|
511
|
+
n.must_be_kind_of(Blather::SASLError)
|
|
512
|
+
n.name.must_equal :not_authorized
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
mocked_server(5) do |val, server|
|
|
516
|
+
case state
|
|
517
|
+
when nil
|
|
518
|
+
state = :started
|
|
519
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
520
|
+
server.send_data "<stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism><mechanism>PLAIN</mechanism><mechanism>ANONYMOUS</mechanism></mechanisms></stream:features>"
|
|
521
|
+
val.must_match(/stream:stream/)
|
|
522
|
+
|
|
523
|
+
when :started
|
|
524
|
+
state = :failed_md5
|
|
525
|
+
server.send_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized /></failure>"
|
|
526
|
+
val.must_match(/mechanism="DIGEST-MD5"/)
|
|
527
|
+
|
|
528
|
+
when :failed_md5
|
|
529
|
+
state = :failed_plain
|
|
530
|
+
server.send_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized /></failure>"
|
|
531
|
+
val.must_match(/mechanism="PLAIN"/)
|
|
532
|
+
|
|
533
|
+
when :failed_plain
|
|
534
|
+
state = :failed_anon
|
|
535
|
+
server.send_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized /></failure>"
|
|
536
|
+
val.must_match(/mechanism="ANONYMOUS"/)
|
|
537
|
+
|
|
538
|
+
when :failed_anon
|
|
539
|
+
EM.stop
|
|
540
|
+
state = :complete
|
|
541
|
+
val.must_match(/\/stream:stream/)
|
|
542
|
+
|
|
543
|
+
else
|
|
544
|
+
EM.stop
|
|
545
|
+
false
|
|
546
|
+
|
|
547
|
+
end
|
|
548
|
+
end
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
it 'tries each mechanism until it succeeds' do
|
|
552
|
+
state = nil
|
|
553
|
+
mocked_server(4) do |val, server|
|
|
554
|
+
case state
|
|
555
|
+
when nil
|
|
556
|
+
state = :started
|
|
557
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'><stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism><mechanism>PLAIN</mechanism><mechanism>ANONYMOUS</mechanism></mechanisms></stream:features>"
|
|
558
|
+
val.must_match(/stream:stream/)
|
|
559
|
+
|
|
560
|
+
when :started
|
|
561
|
+
state = :failed_md5
|
|
562
|
+
server.send_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized /></failure>"
|
|
563
|
+
val.must_match(/mechanism="DIGEST-MD5"/)
|
|
564
|
+
|
|
565
|
+
when :failed_md5
|
|
566
|
+
state = :plain_sent
|
|
567
|
+
server.send_data "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />"
|
|
568
|
+
val.must_match(/mechanism="PLAIN"/)
|
|
569
|
+
|
|
570
|
+
when :plain_sent
|
|
571
|
+
EM.stop
|
|
572
|
+
val.must_match(/stream:stream/)
|
|
573
|
+
|
|
574
|
+
else
|
|
575
|
+
EM.stop
|
|
576
|
+
false
|
|
577
|
+
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
it 'will ignore methods it does not understand' do
|
|
583
|
+
state = nil
|
|
584
|
+
mocked_server(3) do |val, server|
|
|
585
|
+
case state
|
|
586
|
+
when nil
|
|
587
|
+
state = :started
|
|
588
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
589
|
+
server.send_data "<stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>CRAM-MD5</mechanism><mechanism>PLAIN</mechanism></mechanisms></stream:features>"
|
|
590
|
+
val.must_match(/stream:stream/)
|
|
591
|
+
|
|
592
|
+
when :started
|
|
593
|
+
state = :auth_sent
|
|
594
|
+
server.send_data "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />"
|
|
595
|
+
val.must_equal('<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">bkBkAG4AcGFzcw==</auth>')
|
|
596
|
+
|
|
597
|
+
when :auth_sent
|
|
598
|
+
EM.stop
|
|
599
|
+
state = :complete
|
|
600
|
+
val.must_match(/stream:stream/)
|
|
601
|
+
|
|
602
|
+
else
|
|
603
|
+
EM.stop
|
|
604
|
+
false
|
|
605
|
+
|
|
606
|
+
end
|
|
607
|
+
end
|
|
608
|
+
end
|
|
609
|
+
=begin
|
|
610
|
+
it 'sends client an error when an unknown mechanism is sent' do
|
|
611
|
+
@client = mock()
|
|
612
|
+
@client.expects(:receive_data).with { |v| v.must_be_kind_of(Blather::Stream::SASL::UnknownMechanism) }
|
|
613
|
+
started = false
|
|
614
|
+
mocked_server(2) do |val, server|
|
|
615
|
+
if !started
|
|
616
|
+
started = true
|
|
617
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
618
|
+
server.send_data "<stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>UNKNOWN</mechanism></mechanisms></stream:features>"
|
|
619
|
+
val.must_match(/stream:stream/)
|
|
620
|
+
|
|
621
|
+
else
|
|
622
|
+
EM.stop
|
|
623
|
+
val.must_match(/failure(.*)invalid\-mechanism/)
|
|
624
|
+
|
|
625
|
+
end
|
|
626
|
+
end
|
|
627
|
+
end
|
|
628
|
+
=end
|
|
629
|
+
%w[ aborted
|
|
630
|
+
incorrect-encoding
|
|
631
|
+
invalid-authzid
|
|
632
|
+
invalid-mechanism
|
|
633
|
+
mechanism-too-weak
|
|
634
|
+
not-authorized
|
|
635
|
+
temporary-auth-failure
|
|
636
|
+
].each do |error_type|
|
|
637
|
+
it "fails on #{error_type}" do
|
|
638
|
+
@client = mock()
|
|
639
|
+
@client.expects(:receive_data).with do |n|
|
|
640
|
+
n.name.must_equal error_type.gsub('-','_').to_sym
|
|
641
|
+
end
|
|
642
|
+
state = nil
|
|
643
|
+
mocked_server(3) do |val, server|
|
|
644
|
+
case state
|
|
645
|
+
when nil
|
|
646
|
+
state = :started
|
|
647
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'><stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>PLAIN</mechanism></mechanisms></stream:features>"
|
|
648
|
+
val.must_match(/stream:stream/)
|
|
649
|
+
|
|
650
|
+
when :started
|
|
651
|
+
state = :auth_sent
|
|
652
|
+
server.send_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><#{error_type} /></failure>"
|
|
653
|
+
val.must_equal('<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">bkBkAG4AcGFzcw==</auth>')
|
|
654
|
+
|
|
655
|
+
when :auth_sent
|
|
656
|
+
EM.stop
|
|
657
|
+
state = :complete
|
|
658
|
+
val.must_match(/\/stream:stream/)
|
|
659
|
+
|
|
660
|
+
else
|
|
661
|
+
EM.stop
|
|
662
|
+
false
|
|
663
|
+
|
|
664
|
+
end
|
|
665
|
+
end
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
it 'fails when an unkown node comes through during SASL negotiation' do
|
|
670
|
+
@client = mock()
|
|
671
|
+
@client.expects(:receive_data).with do |n|
|
|
672
|
+
n.must_be_instance_of Blather::UnknownResponse
|
|
673
|
+
n.node.element_name.must_equal 'foo-bar'
|
|
674
|
+
end
|
|
675
|
+
state = nil
|
|
676
|
+
mocked_server(3) do |val, server|
|
|
677
|
+
case state
|
|
678
|
+
when nil
|
|
679
|
+
state = :started
|
|
680
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'><stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>PLAIN</mechanism></mechanisms></stream:features>"
|
|
681
|
+
val.must_match(/stream:stream/)
|
|
682
|
+
|
|
683
|
+
when :started
|
|
684
|
+
state = :auth_sent
|
|
685
|
+
server.send_data "<foo-bar />"
|
|
686
|
+
val.must_equal('<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">bkBkAG4AcGFzcw==</auth>')
|
|
687
|
+
|
|
688
|
+
when :auth_sent
|
|
689
|
+
EM.stop
|
|
690
|
+
state = :complete
|
|
691
|
+
val.must_match(/\/stream:stream/)
|
|
692
|
+
|
|
693
|
+
else
|
|
694
|
+
EM.stop
|
|
695
|
+
false
|
|
696
|
+
|
|
697
|
+
end
|
|
698
|
+
end
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
it 'will bind to a resource set by the server' do
|
|
702
|
+
state = nil
|
|
703
|
+
class Client; attr_accessor :jid; end
|
|
704
|
+
@client = Client.new
|
|
705
|
+
@jid = Blather::JID.new('n@d')
|
|
706
|
+
|
|
707
|
+
mocked_server(3) do |val, server|
|
|
708
|
+
case state
|
|
709
|
+
when nil
|
|
710
|
+
state = :started
|
|
711
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
712
|
+
server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
|
|
713
|
+
val.must_match(/stream:stream/)
|
|
714
|
+
|
|
715
|
+
when :started
|
|
716
|
+
state = :complete
|
|
717
|
+
val =~ %r{<iq[^>]+id="([^"]+)"}
|
|
718
|
+
server.send_data "<iq type='result' id='#{$1}'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>#{@jid}/server_resource</jid></bind></iq>"
|
|
719
|
+
server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
|
|
720
|
+
val.must_match(%r{<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"\s?/>})
|
|
721
|
+
|
|
722
|
+
when :complete
|
|
723
|
+
EM.stop
|
|
724
|
+
@stream.jid.must_equal Blather::JID.new('n@d/server_resource')
|
|
725
|
+
|
|
726
|
+
else
|
|
727
|
+
EM.stop
|
|
728
|
+
false
|
|
729
|
+
|
|
730
|
+
end
|
|
731
|
+
end
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
it 'will bind to a resource set by the client' do
|
|
735
|
+
state = nil
|
|
736
|
+
class Client; attr_accessor :jid; end
|
|
737
|
+
@client = Client.new
|
|
738
|
+
@jid = Blather::JID.new('n@d/r')
|
|
739
|
+
|
|
740
|
+
mocked_server(3) do |val, server|
|
|
741
|
+
case state
|
|
742
|
+
when nil
|
|
743
|
+
state = :started
|
|
744
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'><stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
|
|
745
|
+
val.must_match(/stream:stream/)
|
|
746
|
+
|
|
747
|
+
when :started
|
|
748
|
+
state = :complete
|
|
749
|
+
doc = parse_stanza val
|
|
750
|
+
doc.xpath('/iq/bind_ns:bind/bind_ns:resource[.="r"]', :bind_ns => Blather::Stream::Resource::BIND_NS).wont_be_empty
|
|
751
|
+
|
|
752
|
+
server.send_data "<iq type='result' id='#{doc.find_first('iq')['id']}'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>#{@jid}</jid></bind></iq>"
|
|
753
|
+
server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
|
|
754
|
+
|
|
755
|
+
when :complete
|
|
756
|
+
EM.stop
|
|
757
|
+
@stream.jid.must_equal Blather::JID.new('n@d/r')
|
|
758
|
+
|
|
759
|
+
else
|
|
760
|
+
EM.stop
|
|
761
|
+
false
|
|
762
|
+
|
|
763
|
+
end
|
|
764
|
+
end
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
it 'will error out if the bind ID mismatches' do
|
|
768
|
+
state = nil
|
|
769
|
+
@jid = Blather::JID.new('n@d')
|
|
770
|
+
@client = mock()
|
|
771
|
+
|
|
772
|
+
mocked_server(3) do |val, server|
|
|
773
|
+
case state
|
|
774
|
+
when nil
|
|
775
|
+
state = :started
|
|
776
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
777
|
+
server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
|
|
778
|
+
val.must_match(/stream:stream/)
|
|
779
|
+
|
|
780
|
+
when :started
|
|
781
|
+
state = :complete
|
|
782
|
+
val =~ %r{<iq[^>]+id="([^"]+)"}
|
|
783
|
+
@client.expects(:receive_data).with("BIND result ID mismatch. Expected: #{$1}. Received: #{$1}-bad")
|
|
784
|
+
server.send_data "<iq type='result' id='#{$1}-bad'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>#{@jid}/server_resource</jid></bind></iq>"
|
|
785
|
+
val.must_match(%r{<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"\s?/>})
|
|
786
|
+
|
|
787
|
+
when :complete
|
|
788
|
+
EM.stop
|
|
789
|
+
true
|
|
790
|
+
|
|
791
|
+
else
|
|
792
|
+
EM.stop
|
|
793
|
+
false
|
|
794
|
+
|
|
795
|
+
end
|
|
796
|
+
end
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
it 'will return an error if resource binding errors out' do
|
|
800
|
+
state = nil
|
|
801
|
+
@client = mock()
|
|
802
|
+
@client.expects(:receive_data).with do |n|
|
|
803
|
+
n.name.must_equal :bad_request
|
|
804
|
+
end
|
|
805
|
+
mocked_server(3) do |val, server|
|
|
806
|
+
case state
|
|
807
|
+
when nil
|
|
808
|
+
state = :started
|
|
809
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'><stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
|
|
810
|
+
val.must_match(/stream:stream/)
|
|
811
|
+
|
|
812
|
+
when :started
|
|
813
|
+
state = :complete
|
|
814
|
+
doc = parse_stanza val
|
|
815
|
+
doc.xpath('/iq/bind_ns:bind/bind_ns:resource[.="r"]', :bind_ns => Blather::Stream::Resource::BIND_NS).wont_be_empty
|
|
816
|
+
server.send_data "<iq type='error' id='#{doc.find_first('iq')['id']}'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>r</resource></bind><error type='modify'><bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></iq>"
|
|
817
|
+
|
|
818
|
+
when :complete
|
|
819
|
+
EM.stop
|
|
820
|
+
val.must_match(/\/stream:stream/)
|
|
821
|
+
|
|
822
|
+
else
|
|
823
|
+
EM.stop
|
|
824
|
+
false
|
|
825
|
+
|
|
826
|
+
end
|
|
827
|
+
end
|
|
828
|
+
end
|
|
829
|
+
|
|
830
|
+
it 'will return an error if an unkown node comes through during resouce binding' do
|
|
831
|
+
state = nil
|
|
832
|
+
@client = mock()
|
|
833
|
+
@client.expects(:receive_data).with do |n|
|
|
834
|
+
n.must_be_instance_of Blather::UnknownResponse
|
|
835
|
+
n.node.element_name.must_equal 'foo-bar'
|
|
836
|
+
end
|
|
837
|
+
mocked_server(3) do |val, server|
|
|
838
|
+
case state
|
|
839
|
+
when nil
|
|
840
|
+
state = :started
|
|
841
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
842
|
+
server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
|
|
843
|
+
val.must_match(/stream:stream/)
|
|
844
|
+
|
|
845
|
+
when :started
|
|
846
|
+
state = :complete
|
|
847
|
+
doc = parse_stanza val
|
|
848
|
+
doc.xpath('/iq/bind_ns:bind/bind_ns:resource[.="r"]', :bind_ns => Blather::Stream::Resource::BIND_NS).wont_be_empty
|
|
849
|
+
server.send_data "<foo-bar />"
|
|
850
|
+
|
|
851
|
+
when :complete
|
|
852
|
+
EM.stop
|
|
853
|
+
val.must_match(/\/stream:stream/)
|
|
854
|
+
|
|
855
|
+
else
|
|
856
|
+
EM.stop
|
|
857
|
+
false
|
|
858
|
+
|
|
859
|
+
end
|
|
860
|
+
end
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
it 'will establish a session if requested' do
|
|
864
|
+
state = nil
|
|
865
|
+
@client = mock()
|
|
866
|
+
@client.expects(:post_init)
|
|
867
|
+
|
|
868
|
+
mocked_server(3) do |val, server|
|
|
869
|
+
case state
|
|
870
|
+
when nil
|
|
871
|
+
state = :started
|
|
872
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
873
|
+
server.send_data "<stream:features><session xmlns='urn:ietf:params:xml:ns:xmpp-session' /></stream:features>"
|
|
874
|
+
val.must_match(/stream:stream/)
|
|
875
|
+
|
|
876
|
+
when :started
|
|
877
|
+
state = :completed
|
|
878
|
+
doc = parse_stanza val
|
|
879
|
+
doc.find('/iq[@type="set" and @to="d"]/sess_ns:session', :sess_ns => Blather::Stream::Session::SESSION_NS).wont_be_empty
|
|
880
|
+
server.send_data "<iq from='d' type='result' id='#{doc.find_first('iq')['id']}' />"
|
|
881
|
+
server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
|
|
882
|
+
|
|
883
|
+
when :completed
|
|
884
|
+
EM.stop
|
|
885
|
+
true
|
|
886
|
+
|
|
887
|
+
else
|
|
888
|
+
EM.stop
|
|
889
|
+
false
|
|
890
|
+
|
|
891
|
+
end
|
|
892
|
+
end
|
|
893
|
+
end
|
|
894
|
+
|
|
895
|
+
it 'will return an error if session establishment errors out' do
|
|
896
|
+
state = nil
|
|
897
|
+
@client = mock()
|
|
898
|
+
@client.expects(:receive_data).with do |n|
|
|
899
|
+
n.name.must_equal :internal_server_error
|
|
900
|
+
end
|
|
901
|
+
mocked_server(3) do |val, server|
|
|
902
|
+
case state
|
|
903
|
+
when nil
|
|
904
|
+
state = :started
|
|
905
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
906
|
+
server.send_data "<stream:features><session xmlns='urn:ietf:params:xml:ns:xmpp-session' /></stream:features>"
|
|
907
|
+
val.must_match(/stream:stream/)
|
|
908
|
+
|
|
909
|
+
when :started
|
|
910
|
+
state = :completed
|
|
911
|
+
doc = parse_stanza val
|
|
912
|
+
doc.find('/iq[@type="set" and @to="d"]/sess_ns:session', :sess_ns => Blather::Stream::Session::SESSION_NS).wont_be_empty
|
|
913
|
+
server.send_data "<iq from='d' type='error' id='#{doc.find_first('iq')['id']}'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/><error type='wait'><internal-server-error xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></iq>"
|
|
914
|
+
|
|
915
|
+
when :completed
|
|
916
|
+
EM.stop
|
|
917
|
+
val.must_match(/\/stream:stream/)
|
|
918
|
+
|
|
919
|
+
else
|
|
920
|
+
EM.stop
|
|
921
|
+
false
|
|
922
|
+
|
|
923
|
+
end
|
|
924
|
+
end
|
|
925
|
+
end
|
|
926
|
+
|
|
927
|
+
it 'will return an error if an unknown node come through during session establishment' do
|
|
928
|
+
state = nil
|
|
929
|
+
@client = mock()
|
|
930
|
+
@client.expects(:receive_data).with do |n|
|
|
931
|
+
n.must_be_instance_of Blather::UnknownResponse
|
|
932
|
+
n.node.element_name.must_equal 'foo-bar'
|
|
933
|
+
end
|
|
934
|
+
mocked_server(3) do |val, server|
|
|
935
|
+
case state
|
|
936
|
+
when nil
|
|
937
|
+
state = :started
|
|
938
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
|
|
939
|
+
server.send_data "<stream:features><session xmlns='urn:ietf:params:xml:ns:xmpp-session' /></stream:features>"
|
|
940
|
+
val.must_match(/stream:stream/)
|
|
941
|
+
|
|
942
|
+
when :started
|
|
943
|
+
state = :completed
|
|
944
|
+
doc = parse_stanza val
|
|
945
|
+
doc.find('/iq[@type="set" and @to="d"]/sess_ns:session', :sess_ns => Blather::Stream::Session::SESSION_NS).wont_be_empty
|
|
946
|
+
server.send_data '<foo-bar />'
|
|
947
|
+
|
|
948
|
+
when :completed
|
|
949
|
+
EM.stop
|
|
950
|
+
val.must_match(/\/stream:stream/)
|
|
951
|
+
|
|
952
|
+
else
|
|
953
|
+
EM.stop
|
|
954
|
+
false
|
|
955
|
+
|
|
956
|
+
end
|
|
957
|
+
end
|
|
958
|
+
end
|
|
959
|
+
|
|
960
|
+
it 'sends client an error and reply to the server on parse error' do
|
|
961
|
+
@client = mock()
|
|
962
|
+
@client.expects(:receive_data).with do |v|
|
|
963
|
+
v.must_be_kind_of Blather::ParseError
|
|
964
|
+
v.message.must_match(/generate\-parse\-error/)
|
|
965
|
+
end
|
|
966
|
+
state = nil
|
|
967
|
+
mocked_server(3) do |val, server|
|
|
968
|
+
case state
|
|
969
|
+
when nil
|
|
970
|
+
state = :started
|
|
971
|
+
server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"
|
|
972
|
+
server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
|
|
973
|
+
val.must_match(/stream:stream/)
|
|
974
|
+
|
|
975
|
+
when :started
|
|
976
|
+
state = :parse_error
|
|
977
|
+
server.send_data "</generate-parse-error>"
|
|
978
|
+
|
|
979
|
+
when :parse_error
|
|
980
|
+
EM.stop
|
|
981
|
+
val.must_equal "<stream:error><xml-not-well-formed xmlns='urn:ietf:params:xml:ns:xmpp-streams'/></stream:error></stream:stream>"
|
|
982
|
+
|
|
983
|
+
else
|
|
984
|
+
EM.stop
|
|
985
|
+
false
|
|
986
|
+
|
|
987
|
+
end
|
|
988
|
+
end
|
|
989
|
+
end
|
|
990
|
+
|
|
991
|
+
it 'sends stanzas to the wire ensuring "from" is the full JID if set' do
|
|
992
|
+
client = mock()
|
|
993
|
+
client.stubs(:jid)
|
|
994
|
+
client.stubs(:jid=)
|
|
995
|
+
msg = Blather::Stanza::Message.new 'to@jid.com', 'body'
|
|
996
|
+
msg.from = 'node@jid.com'
|
|
997
|
+
comp = Blather::Stream::Client.new nil, client, 'node@jid.com/resource', 'pass'
|
|
998
|
+
comp.expects(:send_data).with { |s| s.must_match(/^<message[^>]*from="node@jid\.com\/resource"/) }
|
|
999
|
+
comp.send msg
|
|
1000
|
+
end
|
|
1001
|
+
|
|
1002
|
+
it 'sends stanzas to the wire leaving "from" nil if not set' do
|
|
1003
|
+
client = mock()
|
|
1004
|
+
client.stubs(:jid)
|
|
1005
|
+
client.stubs(:jid=)
|
|
1006
|
+
msg = Blather::Stanza::Message.new 'to@jid.com', 'body'
|
|
1007
|
+
comp = Blather::Stream::Client.new nil, client, 'node@jid.com/resource', 'pass'
|
|
1008
|
+
comp.expects(:send_data).with { |s| s.wont_match(/^<message[^>]*from=/); true }
|
|
1009
|
+
comp.send msg
|
|
1010
|
+
end
|
|
1011
|
+
end
|