sprsquish-blather 0.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. data/LICENSE +2 -0
  2. data/README.rdoc +100 -0
  3. data/Rakefile +110 -0
  4. data/examples/drb_client.rb +5 -0
  5. data/examples/echo.rb +18 -0
  6. data/ext/extconf.rb +65 -0
  7. data/ext/push_parser.c +231 -0
  8. data/lib/blather/client.rb +219 -44
  9. data/lib/blather/{core/sugar.rb → core_ext/active_support.rb} +25 -13
  10. data/lib/blather/core_ext/libxml.rb +28 -0
  11. data/lib/blather/errors/sasl_error.rb +87 -0
  12. data/lib/blather/errors/stanza_error.rb +262 -0
  13. data/lib/blather/errors/stream_error.rb +253 -0
  14. data/lib/blather/errors.rb +48 -0
  15. data/lib/blather/{core/jid.rb → jid.rb} +15 -26
  16. data/lib/blather/{core/roster.rb → roster.rb} +22 -0
  17. data/lib/blather/{core/roster_item.rb → roster_item.rb} +39 -8
  18. data/lib/blather/stanza/iq/disco.rb +11 -0
  19. data/lib/blather/stanza/iq/discos/disco_info.rb +86 -0
  20. data/lib/blather/stanza/iq/discos/disco_items.rb +61 -0
  21. data/lib/blather/stanza/iq/query.rb +51 -0
  22. data/lib/blather/stanza/iq/roster.rb +90 -0
  23. data/lib/blather/stanza/iq.rb +38 -0
  24. data/lib/blather/stanza/message.rb +58 -0
  25. data/lib/blather/stanza/presence/status.rb +78 -0
  26. data/lib/blather/stanza/presence/subscription.rb +72 -0
  27. data/lib/blather/stanza/presence.rb +45 -0
  28. data/lib/blather/stanza.rb +101 -0
  29. data/lib/blather/stream/client.rb +26 -0
  30. data/lib/blather/stream/component.rb +34 -0
  31. data/lib/blather/stream/parser.rb +70 -0
  32. data/lib/blather/stream/resource.rb +48 -0
  33. data/lib/blather/stream/sasl.rb +173 -0
  34. data/lib/blather/stream/session.rb +36 -0
  35. data/lib/blather/stream/stream_handler.rb +39 -0
  36. data/lib/blather/stream/tls.rb +33 -0
  37. data/lib/blather/stream.rb +249 -0
  38. data/lib/blather/xmpp_node.rb +199 -0
  39. data/lib/blather.rb +40 -41
  40. data/spec/blather/core_ext/libxml_spec.rb +58 -0
  41. data/spec/blather/errors/sasl_error_spec.rb +56 -0
  42. data/spec/blather/errors/stanza_error_spec.rb +148 -0
  43. data/spec/blather/errors/stream_error_spec.rb +114 -0
  44. data/spec/blather/errors_spec.rb +40 -0
  45. data/spec/blather/{core/jid_spec.rb → jid_spec.rb} +9 -1
  46. data/spec/blather/{core/roster_item_spec.rb → roster_item_spec.rb} +6 -1
  47. data/spec/blather/{core/roster_spec.rb → roster_spec.rb} +16 -6
  48. data/spec/blather/stanza/iq/discos/disco_info_spec.rb +207 -0
  49. data/spec/blather/stanza/iq/discos/disco_items_spec.rb +136 -0
  50. data/spec/blather/stanza/iq/query_spec.rb +34 -0
  51. data/spec/blather/stanza/iq/roster_spec.rb +123 -0
  52. data/spec/blather/stanza/iq_spec.rb +40 -0
  53. data/spec/blather/stanza/message_spec.rb +52 -0
  54. data/spec/blather/stanza/presence/status_spec.rb +102 -0
  55. data/spec/blather/stanza/presence/subscription_spec.rb +85 -0
  56. data/spec/blather/stanza/presence_spec.rb +53 -0
  57. data/spec/blather/{core/stanza_spec.rb → stanza_spec.rb} +14 -2
  58. data/spec/blather/stream/client_spec.rb +787 -0
  59. data/spec/blather/stream/component_spec.rb +86 -0
  60. data/spec/blather/{core/xmpp_node_spec.rb → xmpp_node_spec.rb} +76 -23
  61. data/spec/build_safe.rb +20 -0
  62. data/spec/spec_helper.rb +7 -17
  63. metadata +79 -59
  64. data/CHANGELOG +0 -1
  65. data/blather.gemspec +0 -73
  66. data/lib/blather/callback.rb +0 -24
  67. data/lib/blather/core/errors.rb +0 -24
  68. data/lib/blather/core/stanza.rb +0 -90
  69. data/lib/blather/core/stream.rb +0 -179
  70. data/lib/blather/core/xmpp_node.rb +0 -95
  71. data/lib/blather/extensions/last_activity.rb +0 -57
  72. data/lib/blather/extensions/version.rb +0 -85
  73. data/spec/blather/core/stream_spec.rb +0 -263
@@ -0,0 +1,787 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
2
+
3
+ describe 'Blather::Stream::Client' do
4
+ class MockServer; end
5
+ module ServerMock
6
+ def receive_data(data)
7
+ @server ||= MockServer.new
8
+ @server.receive_data data, self
9
+ end
10
+ end
11
+
12
+ def mocked_server(times = nil, &block)
13
+ @client ||= mock()
14
+ @client.stubs(:stopped) unless @client.respond_to?(:stopped)
15
+ @client.stubs(:jid=) unless @client.respond_to?(:jid=)
16
+
17
+ MockServer.any_instance.expects(:receive_data).send(*(times ? [:times, times] : [:at_least, 1])).with &block
18
+ EventMachine::run {
19
+ # Mocked server
20
+ EventMachine::start_server '127.0.0.1', 12345, ServerMock
21
+
22
+ # Stream connection
23
+ EM.connect('127.0.0.1', 12345, Stream::Client, @client, @jid || JID.new('n@d/r'), 'pass') { |c| @stream = c }
24
+ }
25
+ end
26
+
27
+ it 'can be started' do
28
+ client = mock()
29
+ params = [client, 'n@d/r', 'pass', 'host', 1234]
30
+ EM.expects(:connect).with do |*parms|
31
+ parms[0] == 'host' &&
32
+ parms[1] == 1234 &&
33
+ parms[3] == client &&
34
+ parms[5] == 'pass' &&
35
+ parms[4] == JID.new('n@d/r')
36
+ end
37
+
38
+ Stream::Client.start *(params)
39
+ end
40
+
41
+ it 'can figure out the host to use based on the jid' do
42
+ client = mock()
43
+ params = [client, 'n@d/r', 'pass', 'd', 5222]
44
+ EM.expects(:connect).with do |*parms|
45
+ parms[0] == 'd' &&
46
+ parms[1] == 5222 &&
47
+ parms[3] == client &&
48
+ parms[5] == 'pass' &&
49
+ parms[4] == JID.new('n@d/r')
50
+ end
51
+
52
+ Stream::Client.start client, 'n@d/r', 'pass'
53
+ end
54
+
55
+ it 'starts the stream once the connection is complete' do
56
+ mocked_server(1) { |val, _| EM.stop; val.must_match(/stream:stream/) }
57
+ end
58
+
59
+ it 'sends stanzas to the client when the stream is ready' do
60
+ @client = mock()
61
+ @client.expects(:call).with do |n|
62
+ EM.stop
63
+ n.kind_of?(Stanza::Message) && @stream.ready?.must_equal(true)
64
+ end
65
+
66
+ mocked_server(1) do |val, server|
67
+ val.must_match(/stream:stream/)
68
+ server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
69
+ server.send_data "<message to='a@b/c' from='d@e/f' type='chat' xml:lang='en'><body>Message!</body></message>"
70
+ end
71
+ end
72
+
73
+ it 'puts itself in the stopped state and calls @client.stopped when stopped' do
74
+ @client = mock()
75
+ @client.expects(:stopped).at_least_once
76
+
77
+ started = false
78
+ mocked_server(2) do |val, server|
79
+ if !started
80
+ started = true
81
+ server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
82
+ server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
83
+ val.must_match(/stream:stream/)
84
+
85
+ else
86
+ EM.stop
87
+ @stream.stopped?.must_equal false
88
+ @stream.unbind
89
+ @stream.stopped?.must_equal true
90
+
91
+ end
92
+ end
93
+ end
94
+
95
+ it 'will be in the negotiating state during feature negotiations' do
96
+ state = nil
97
+ @client = mock()
98
+ @client.stubs(:stream_started)
99
+ @client.expects(:call).with do |n|
100
+ EM.stop
101
+ state.must_equal(:negotiated) && @stream.negotiating?.must_equal(false)
102
+ end
103
+
104
+ mocked_server(2) do |val, server|
105
+ case state
106
+ when nil
107
+ state = :started
108
+ server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
109
+ server.send_data "<stream:features><session xmlns='urn:ietf:params:xml:ns:xmpp-session' /></stream:features>"
110
+ true
111
+
112
+ when :started
113
+ state = :negotiated
114
+ @stream.negotiating?.must_equal(true)
115
+ server.send_data "<iq from='d' type='result' id='#{val[/id="([^"]+)"/,1]}' />"
116
+ server.send_data "<message to='a@b/c' from='d@e/f' type='chat' xml:lang='en'><body>Message!</body></message>"
117
+ true
118
+
119
+ else
120
+ EM.stop
121
+ false
122
+
123
+ end
124
+ end
125
+ end
126
+
127
+ it 'stops when sent </stream:stream>' do
128
+ state = nil
129
+ mocked_server(3) do |val, server|
130
+ case state
131
+ when nil
132
+ state = :started
133
+ 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'>"
134
+ server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
135
+ val.must_match(/stream:stream/)
136
+
137
+ when :started
138
+ state = :stopped
139
+ server.send_data '</stream:stream>'
140
+ @stream.stopped?.must_equal false
141
+
142
+ when :stopped
143
+ EM.stop
144
+ @stream.stopped?.must_equal true
145
+ val.must_equal '</stream:stream>'
146
+
147
+ else
148
+ EM.stop
149
+ false
150
+
151
+ end
152
+ end
153
+ end
154
+
155
+ it 'sends client an error on stream:error' do
156
+ @client = mock()
157
+ @client.expects(:call).with do |v|
158
+ v.must_be_instance_of(StreamError::Conflict)
159
+ v.text.must_equal 'Already signed in'
160
+ v.to_s.must_equal "Stream Error (conflict): #{v.text}"
161
+ end
162
+
163
+ state = nil
164
+ mocked_server(3) do |val, server|
165
+ case state
166
+ when nil
167
+ state = :started
168
+ server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"
169
+ server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
170
+ val.must_match(/stream:stream/)
171
+
172
+ when :started
173
+ state = :stopped
174
+ server.send_data "<stream:error><conflict xmlns='urn:ietf:params:xml:ns:xmpp-streams' />"
175
+ server.send_data "<text xmlns='urn:ietf:params:xml:ns:xmpp-streams'>Already signed in</text></stream:error>"
176
+
177
+ when :stopped
178
+ EM.stop
179
+ val.must_equal "</stream:stream>"
180
+
181
+ else
182
+ EM.stop
183
+ false
184
+
185
+ end
186
+ end
187
+ end
188
+
189
+ it 'starts TLS when asked' do
190
+ state = nil
191
+ mocked_server(3) do |val, server|
192
+ case state
193
+ when nil
194
+ state = :started
195
+ 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>"
196
+ val.must_match(/stream:stream/)
197
+
198
+ when :started
199
+ state = :tls
200
+ @stream.expects(:start_tls)
201
+ server.send_data "<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
202
+ val.must_match(/starttls/)
203
+
204
+ when :tls
205
+ EM.stop
206
+ true
207
+
208
+ else
209
+ EM.stop
210
+ false
211
+
212
+ end
213
+ end
214
+ end
215
+
216
+ it 'will fail if TLS negotiation fails' do
217
+ state = nil
218
+ @client = mock()
219
+ @client.expects(:call).with { |v| v.must_be_kind_of TLSFailure }
220
+ mocked_server(3) do |val, server|
221
+ case state
222
+ when nil
223
+ state = :started
224
+ 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>"
225
+ val.must_match(/stream:stream/)
226
+
227
+ when :started
228
+ state = :tls
229
+ @stream.expects(:start_tls).never
230
+ server.send_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/></stream:stream>"
231
+ val.must_match(/starttls/)
232
+
233
+ when :tls
234
+ EM.stop
235
+ val.must_equal "</stream:stream>"
236
+
237
+ else
238
+ EM.stop
239
+ false
240
+
241
+ end
242
+ end
243
+ end
244
+
245
+ it 'will fail if a bad node comes through TLS negotiations' do
246
+ state = nil
247
+ @client = mock()
248
+ @client.expects(:call).with do |v|
249
+ v.must_be_kind_of UnknownResponse
250
+ v.node.element_name.must_equal 'foo-bar'
251
+ end
252
+ mocked_server(3) do |val, server|
253
+ case state
254
+ when nil
255
+ state = :started
256
+ 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>"
257
+ val.must_match(/stream:stream/)
258
+
259
+ when :started
260
+ state = :tls
261
+ @stream.expects(:start_tls).never
262
+ server.send_data "<foo-bar xmlns='urn:ietf:params:xml:ns:xmpp-tls'/></stream:stream>"
263
+ val.must_match(/starttls/)
264
+
265
+ when :tls
266
+ EM.stop
267
+ val.must_equal "</stream:stream>"
268
+
269
+ else
270
+ EM.stop
271
+ false
272
+
273
+ end
274
+ end
275
+ end
276
+
277
+ it 'connects via SASL MD5 when asked' do
278
+ Time.any_instance.stubs(:to_f).returns(1.1)
279
+ state = nil
280
+
281
+ mocked_server(5) do |val, server|
282
+ case state
283
+ when nil
284
+ state = :started
285
+ 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>"
286
+ val.must_match(/stream:stream/)
287
+
288
+ when :started
289
+ state = :auth_sent
290
+ server.send_data "<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNzCg==</challenge>"
291
+ val.must_match(/auth.*DIGEST\-MD5/)
292
+
293
+ when :auth_sent
294
+ state = :response1_sent
295
+ server.send_data "<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZAo=</challenge>"
296
+ val.must_equal('<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">bm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixjaGFyc2V0PXV0Zi04LHVzZXJuYW1lPSJuIixyZWFsbT0ic29tZXJlYWxtIixjbm9uY2U9Ijc3N2Q0NWJiYmNkZjUwZDQ5YzQyYzcwYWQ3YWNmNWZlIixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJ4bXBwL2QiLHJlc3BvbnNlPTZiNTlhY2Q1ZWJmZjhjZTA0NTYzMGFiMDU2Zjg3MTdm</response>')
297
+
298
+ when :response1_sent
299
+ state = :response2_sent
300
+ server.send_data "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />"
301
+ val.must_match(%r{<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl"\s?/>})
302
+
303
+ when :response2_sent
304
+ EM.stop
305
+ state = :complete
306
+ val.must_match(/stream:stream/)
307
+
308
+ else
309
+ EM.stop
310
+ false
311
+
312
+ end
313
+ end
314
+ end
315
+
316
+ it 'will connect via SSL PLAIN when asked' do
317
+ state = nil
318
+ mocked_server(3) do |val, server|
319
+ case state
320
+ when nil
321
+ state = :started
322
+ 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>"
323
+ val.must_match(/stream:stream/)
324
+
325
+ when :started
326
+ state = :auth_sent
327
+ server.send_data "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />"
328
+ val.must_equal('<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">bkBkAG4AcGFzcw==</auth>')
329
+
330
+ when :auth_sent
331
+ EM.stop
332
+ state = :complete
333
+ val.must_match(/stream:stream/)
334
+
335
+ else
336
+ EM.stop
337
+ false
338
+
339
+ end
340
+ end
341
+ end
342
+
343
+ it 'will connect via SSL ANONYMOUS when asked' do
344
+ state = nil
345
+
346
+ mocked_server(3) do |val, server|
347
+ case state
348
+ when nil
349
+ state = :started
350
+ 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>"
351
+ val.must_match(/stream:stream/)
352
+
353
+ when :started
354
+ state = :auth_sent
355
+ server.send_data "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />"
356
+ val.must_equal('<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="ANONYMOUS">bg==</auth>')
357
+
358
+ when :auth_sent
359
+ EM.stop
360
+ state = :complete
361
+ val.must_match(/stream:stream/)
362
+
363
+ else
364
+ EM.stop
365
+ false
366
+
367
+ end
368
+ end
369
+ end
370
+
371
+ it 'tried each possible mechanism until it fails completely' do
372
+ state = nil
373
+ @client = mock()
374
+ @client.expects(:call).with do |n|
375
+ n.must_be_kind_of(SASLError)
376
+ n.must_be_instance_of SASLError::NotAuthorized
377
+ end
378
+
379
+ mocked_server(5) do |val, server|
380
+ case state
381
+ when nil
382
+ state = :started
383
+ 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>"
384
+ val.must_match(/stream:stream/)
385
+
386
+ when :started
387
+ state = :failed_md5
388
+ server.send_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized /></failure>"
389
+ val.must_match(/mechanism="DIGEST-MD5"/)
390
+
391
+ when :failed_md5
392
+ state = :failed_plain
393
+ server.send_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized /></failure>"
394
+ val.must_match(/mechanism="PLAIN"/)
395
+
396
+ when :failed_plain
397
+ state = :failed_anon
398
+ server.send_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized /></failure>"
399
+ val.must_match(/mechanism="ANONYMOUS"/)
400
+
401
+ when :failed_anon
402
+ EM.stop
403
+ state = :complete
404
+ val.must_match(/\/stream:stream/)
405
+
406
+ else
407
+ EM.stop
408
+ false
409
+
410
+ end
411
+ end
412
+ end
413
+
414
+ it 'tries each mechanism until it succeeds' do
415
+ state = nil
416
+ mocked_server(4) do |val, server|
417
+ case state
418
+ when nil
419
+ state = :started
420
+ 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>"
421
+ val.must_match(/stream:stream/)
422
+
423
+ when :started
424
+ state = :failed_md5
425
+ server.send_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized /></failure>"
426
+ val.must_match(/mechanism="DIGEST-MD5"/)
427
+
428
+ when :failed_md5
429
+ state = :plain_sent
430
+ server.send_data "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />"
431
+ val.must_match(/mechanism="PLAIN"/)
432
+
433
+ when :plain_sent
434
+ EM.stop
435
+ val.must_match(/stream:stream/)
436
+
437
+ else
438
+ EM.stop
439
+ false
440
+
441
+ end
442
+ end
443
+ end
444
+
445
+ it 'sends client an error when an unknown mechanism is sent' do
446
+ @client = mock()
447
+ @client.expects(:call).with { |v| v.must_be_kind_of(Stream::SASL::UnknownMechanism) }
448
+ started = false
449
+ mocked_server(2) do |val, server|
450
+ if !started
451
+ started = true
452
+ server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
453
+ server.send_data "<stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>UNKNOWN</mechanism></mechanisms></stream:features>"
454
+ val.must_match(/stream:stream/)
455
+
456
+ else
457
+ EM.stop
458
+ val.must_match(/failure(.*)invalid\-mechanism/)
459
+
460
+ end
461
+ end
462
+ end
463
+
464
+ %w[ aborted
465
+ incorrect-encoding
466
+ invalid-authzid
467
+ invalid-mechanism
468
+ mechanism-too-weak
469
+ not-authorized
470
+ temporary-auth-failure
471
+ ].each do |error_type|
472
+ it "fails on #{error_type}" do
473
+ @client = mock()
474
+ @client.expects(:call).with do |n|
475
+ n.must_be_instance_of SASLError.class_from_registration(error_type)
476
+ end
477
+ state = nil
478
+ mocked_server(3) do |val, server|
479
+ case state
480
+ when nil
481
+ state = :started
482
+ 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>"
483
+ val.must_match(/stream:stream/)
484
+
485
+ when :started
486
+ state = :auth_sent
487
+ server.send_data "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><#{error_type} /></failure>"
488
+ val.must_equal('<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">bkBkAG4AcGFzcw==</auth>')
489
+
490
+ when :auth_sent
491
+ EM.stop
492
+ state = :complete
493
+ val.must_match(/\/stream:stream/)
494
+
495
+ else
496
+ EM.stop
497
+ false
498
+
499
+ end
500
+ end
501
+ end
502
+ end
503
+
504
+ it 'fails when an unkown node comes through during SASL negotiation' do
505
+ @client = mock()
506
+ @client.expects(:call).with do |n|
507
+ n.must_be_instance_of UnknownResponse
508
+ n.node.element_name.must_equal 'foo-bar'
509
+ end
510
+ state = nil
511
+ mocked_server(3) do |val, server|
512
+ case state
513
+ when nil
514
+ state = :started
515
+ 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>"
516
+ val.must_match(/stream:stream/)
517
+
518
+ when :started
519
+ state = :auth_sent
520
+ server.send_data "<foo-bar />"
521
+ val.must_equal('<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">bkBkAG4AcGFzcw==</auth>')
522
+
523
+ when :auth_sent
524
+ EM.stop
525
+ state = :complete
526
+ val.must_match(/\/stream:stream/)
527
+
528
+ else
529
+ EM.stop
530
+ false
531
+
532
+ end
533
+ end
534
+ end
535
+
536
+ it 'will bind to a resource set by the server' do
537
+ state = nil
538
+ class Client; attr_accessor :jid; end
539
+ @client = Client.new
540
+ @jid = JID.new('n@d')
541
+
542
+ mocked_server(3) do |val, server|
543
+ case state
544
+ when nil
545
+ state = :started
546
+ 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>"
547
+ val.must_match(/stream:stream/)
548
+
549
+ when :started
550
+ state = :complete
551
+ val =~ %r{<iq[^>]+id="([^"]+)"}
552
+ server.send_data "<iq type='result' id='#{$1}'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>#{@jid}/server_resource</jid></bind></iq>"
553
+ server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
554
+ val.must_match(%r{<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"\s?/>})
555
+
556
+ when :complete
557
+ EM.stop
558
+ @client.jid.must_equal JID.new('n@d/server_resource')
559
+
560
+ else
561
+ EM.stop
562
+ false
563
+
564
+ end
565
+ end
566
+ end
567
+
568
+ it 'will bind to a resource set by the client' do
569
+ state = nil
570
+ class Client; attr_accessor :jid; end
571
+ @client = Client.new
572
+ @jid = JID.new('n@d/r')
573
+
574
+ mocked_server(3) do |val, server|
575
+ case state
576
+ when nil
577
+ state = :started
578
+ 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>"
579
+ val.must_match(/stream:stream/)
580
+
581
+ when :started
582
+ state = :complete
583
+ val =~ %r{<iq[^>]+id="([^"]+)"}
584
+ server.send_data "<iq type='result' id='#{$1}'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>#{@jid}</jid></bind></iq>"
585
+ server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
586
+ val.must_match(%r{<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>r</resource></bind>})
587
+
588
+ when :complete
589
+ EM.stop
590
+ @client.jid.must_equal JID.new('n@d/r')
591
+
592
+ else
593
+ EM.stop
594
+ false
595
+
596
+ end
597
+ end
598
+ end
599
+
600
+ it 'will return an error if resource binding errors out' do
601
+ state = nil
602
+ @client = mock()
603
+ @client.expects(:call).with do |n|
604
+ n.must_be_instance_of StanzaError::BadRequest
605
+ end
606
+ mocked_server(3) do |val, server|
607
+ case state
608
+ when nil
609
+ state = :started
610
+ 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>"
611
+ val.must_match(/stream:stream/)
612
+
613
+ when :started
614
+ state = :complete
615
+ val =~ %r{<iq[^>]+id="([^"]+)"}
616
+ server.send_data "<iq type='error' id='#{$1}'><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>"
617
+ val.must_match(%r{<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>r</resource></bind>})
618
+
619
+ when :complete
620
+ EM.stop
621
+ val.must_match(/\/stream:stream/)
622
+
623
+ else
624
+ EM.stop
625
+ false
626
+
627
+ end
628
+ end
629
+ end
630
+
631
+ it 'will return an error if an unkown node comes through during resouce binding' do
632
+ state = nil
633
+ @client = mock()
634
+ @client.expects(:call).with do |n|
635
+ n.must_be_instance_of UnknownResponse
636
+ n.node.element_name.must_equal 'foo-bar'
637
+ end
638
+ mocked_server(3) do |val, server|
639
+ case state
640
+ when nil
641
+ state = :started
642
+ 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>"
643
+ val.must_match(/stream:stream/)
644
+
645
+ when :started
646
+ state = :complete
647
+ val =~ %r{<iq[^>]+id="([^"]+)"}
648
+ server.send_data "<foo-bar />"
649
+ val.must_match(%r{<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>r</resource></bind>})
650
+
651
+ when :complete
652
+ EM.stop
653
+ val.must_match(/\/stream:stream/)
654
+
655
+ else
656
+ EM.stop
657
+ false
658
+
659
+ end
660
+ end
661
+ end
662
+
663
+ it 'will establish a session if requested' do
664
+ state = nil
665
+ @client = mock()
666
+ @client.expects(:stream_started)
667
+
668
+ mocked_server(3) do |val, server|
669
+ case state
670
+ when nil
671
+ state = :started
672
+ server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
673
+ server.send_data "<stream:features><session xmlns='urn:ietf:params:xml:ns:xmpp-session' /></stream:features>"
674
+ val.must_match(/stream:stream/)
675
+
676
+ when :started
677
+ state = :completed
678
+ server.send_data "<iq from='d' type='result' id='#{val[/id="([^"]+)"/,1]}' />"
679
+ server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
680
+ val.must_match(%r{<iq id="[^"]+" type="set" to="d"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"\s?/></iq>})
681
+
682
+ when :completed
683
+ EM.stop
684
+ true
685
+
686
+ else
687
+ EM.stop
688
+ false
689
+
690
+ end
691
+ end
692
+ end
693
+
694
+ it 'will return an error if session establishment errors out' do
695
+ state = nil
696
+ @client = mock()
697
+ @client.expects(:call).with do |n|
698
+ n.must_be_instance_of StanzaError::InternalServerError
699
+ end
700
+ mocked_server(3) do |val, server|
701
+ case state
702
+ when nil
703
+ state = :started
704
+ server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
705
+ server.send_data "<stream:features><session xmlns='urn:ietf:params:xml:ns:xmpp-session' /></stream:features>"
706
+ val.must_match(/stream:stream/)
707
+
708
+ when :started
709
+ state = :completed
710
+ server.send_data "<iq from='d' type='error' id='#{val[/id="([^"]+)"/,1]}'><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>"
711
+ val.must_match(%r{<iq id="[^"]+" type="set" to="d"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"\s?/></iq>})
712
+
713
+ when :completed
714
+ EM.stop
715
+ val.must_match(/\/stream:stream/)
716
+
717
+ else
718
+ EM.stop
719
+ false
720
+
721
+ end
722
+ end
723
+ end
724
+
725
+ it 'will return an error if an unknown node come through during session establishment' do
726
+ state = nil
727
+ @client = mock()
728
+ @client.expects(:call).with do |n|
729
+ n.must_be_instance_of UnknownResponse
730
+ n.node.element_name.must_equal 'foo-bar'
731
+ end
732
+ mocked_server(3) do |val, server|
733
+ case state
734
+ when nil
735
+ state = :started
736
+ server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>"
737
+ server.send_data "<stream:features><session xmlns='urn:ietf:params:xml:ns:xmpp-session' /></stream:features>"
738
+ val.must_match(/stream:stream/)
739
+
740
+ when :started
741
+ state = :completed
742
+ server.send_data '<foo-bar />'
743
+ val.must_match(%r{<iq id="[^"]+" type="set" to="d"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"\s?/></iq>})
744
+
745
+ when :completed
746
+ EM.stop
747
+ val.must_match(/\/stream:stream/)
748
+
749
+ else
750
+ EM.stop
751
+ false
752
+
753
+ end
754
+ end
755
+ end
756
+
757
+ it 'sends client an error on parse error' do
758
+ @client = mock()
759
+ @client.expects(:call).with do |v|
760
+ v.must_be_kind_of ParseError
761
+ v.message.must_match(/generate\-parse\-error/)
762
+ end
763
+ state = nil
764
+ mocked_server(3) do |val, server|
765
+ case state
766
+ when nil
767
+ state = :started
768
+ server.send_data "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"
769
+ server.send_data "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' /></stream:features>"
770
+ val.must_match(/stream:stream/)
771
+
772
+ when :started
773
+ state = :parse_error
774
+ server.send_data "</generate-parse-error>"
775
+
776
+ when :parse_error
777
+ EM.stop
778
+ val.must_equal "</stream:stream>"
779
+
780
+ else
781
+ EM.stop
782
+ false
783
+
784
+ end
785
+ end
786
+ end
787
+ end