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