vines 0.1.0

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 (119) hide show
  1. data/LICENSE +19 -0
  2. data/README +34 -0
  3. data/Rakefile +55 -0
  4. data/bin/vines +95 -0
  5. data/conf/certs/README +32 -0
  6. data/conf/certs/ca-bundle.crt +3987 -0
  7. data/conf/config.rb +114 -0
  8. data/lib/vines.rb +155 -0
  9. data/lib/vines/command/bcrypt.rb +12 -0
  10. data/lib/vines/command/cert.rb +49 -0
  11. data/lib/vines/command/init.rb +58 -0
  12. data/lib/vines/command/ldap.rb +35 -0
  13. data/lib/vines/command/restart.rb +12 -0
  14. data/lib/vines/command/schema.rb +24 -0
  15. data/lib/vines/command/start.rb +28 -0
  16. data/lib/vines/command/stop.rb +18 -0
  17. data/lib/vines/config.rb +191 -0
  18. data/lib/vines/contact.rb +99 -0
  19. data/lib/vines/daemon.rb +78 -0
  20. data/lib/vines/error.rb +150 -0
  21. data/lib/vines/jid.rb +56 -0
  22. data/lib/vines/kit.rb +23 -0
  23. data/lib/vines/router.rb +125 -0
  24. data/lib/vines/stanza.rb +55 -0
  25. data/lib/vines/stanza/iq.rb +50 -0
  26. data/lib/vines/stanza/iq/auth.rb +18 -0
  27. data/lib/vines/stanza/iq/disco_info.rb +25 -0
  28. data/lib/vines/stanza/iq/disco_items.rb +23 -0
  29. data/lib/vines/stanza/iq/error.rb +16 -0
  30. data/lib/vines/stanza/iq/ping.rb +16 -0
  31. data/lib/vines/stanza/iq/query.rb +10 -0
  32. data/lib/vines/stanza/iq/result.rb +16 -0
  33. data/lib/vines/stanza/iq/roster.rb +153 -0
  34. data/lib/vines/stanza/iq/session.rb +22 -0
  35. data/lib/vines/stanza/iq/vcard.rb +58 -0
  36. data/lib/vines/stanza/message.rb +41 -0
  37. data/lib/vines/stanza/presence.rb +119 -0
  38. data/lib/vines/stanza/presence/error.rb +23 -0
  39. data/lib/vines/stanza/presence/probe.rb +38 -0
  40. data/lib/vines/stanza/presence/subscribe.rb +66 -0
  41. data/lib/vines/stanza/presence/subscribed.rb +64 -0
  42. data/lib/vines/stanza/presence/unavailable.rb +15 -0
  43. data/lib/vines/stanza/presence/unsubscribe.rb +57 -0
  44. data/lib/vines/stanza/presence/unsubscribed.rb +50 -0
  45. data/lib/vines/storage.rb +216 -0
  46. data/lib/vines/storage/couchdb.rb +119 -0
  47. data/lib/vines/storage/ldap.rb +59 -0
  48. data/lib/vines/storage/local.rb +66 -0
  49. data/lib/vines/storage/redis.rb +108 -0
  50. data/lib/vines/storage/sql.rb +174 -0
  51. data/lib/vines/store.rb +51 -0
  52. data/lib/vines/stream.rb +198 -0
  53. data/lib/vines/stream/client.rb +131 -0
  54. data/lib/vines/stream/client/auth.rb +94 -0
  55. data/lib/vines/stream/client/auth_restart.rb +33 -0
  56. data/lib/vines/stream/client/bind.rb +58 -0
  57. data/lib/vines/stream/client/bind_restart.rb +25 -0
  58. data/lib/vines/stream/client/closed.rb +13 -0
  59. data/lib/vines/stream/client/ready.rb +15 -0
  60. data/lib/vines/stream/client/start.rb +27 -0
  61. data/lib/vines/stream/client/tls.rb +37 -0
  62. data/lib/vines/stream/component.rb +53 -0
  63. data/lib/vines/stream/component/handshake.rb +25 -0
  64. data/lib/vines/stream/component/ready.rb +24 -0
  65. data/lib/vines/stream/component/start.rb +19 -0
  66. data/lib/vines/stream/http.rb +111 -0
  67. data/lib/vines/stream/http/http_request.rb +22 -0
  68. data/lib/vines/stream/http/http_state.rb +139 -0
  69. data/lib/vines/stream/http/http_states.rb +53 -0
  70. data/lib/vines/stream/parser.rb +78 -0
  71. data/lib/vines/stream/server.rb +126 -0
  72. data/lib/vines/stream/server/auth.rb +13 -0
  73. data/lib/vines/stream/server/auth_restart.rb +19 -0
  74. data/lib/vines/stream/server/final_restart.rb +20 -0
  75. data/lib/vines/stream/server/outbound/auth.rb +31 -0
  76. data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
  77. data/lib/vines/stream/server/outbound/auth_result.rb +28 -0
  78. data/lib/vines/stream/server/outbound/final_features.rb +27 -0
  79. data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
  80. data/lib/vines/stream/server/outbound/start.rb +20 -0
  81. data/lib/vines/stream/server/outbound/tls.rb +30 -0
  82. data/lib/vines/stream/server/outbound/tls_result.rb +31 -0
  83. data/lib/vines/stream/server/ready.rb +20 -0
  84. data/lib/vines/stream/server/start.rb +13 -0
  85. data/lib/vines/stream/server/tls.rb +13 -0
  86. data/lib/vines/stream/state.rb +55 -0
  87. data/lib/vines/token_bucket.rb +46 -0
  88. data/lib/vines/user.rb +124 -0
  89. data/lib/vines/version.rb +5 -0
  90. data/lib/vines/xmpp_server.rb +25 -0
  91. data/test/config_test.rb +396 -0
  92. data/test/error_test.rb +59 -0
  93. data/test/ext/nokogiri.rb +14 -0
  94. data/test/jid_test.rb +71 -0
  95. data/test/kit_test.rb +21 -0
  96. data/test/router_test.rb +60 -0
  97. data/test/stanza/iq/roster_test.rb +198 -0
  98. data/test/stanza/iq/session_test.rb +30 -0
  99. data/test/stanza/iq/vcard_test.rb +159 -0
  100. data/test/stanza/message_test.rb +124 -0
  101. data/test/stanza/presence/subscribe_test.rb +75 -0
  102. data/test/storage/couchdb_test.rb +102 -0
  103. data/test/storage/ldap_test.rb +207 -0
  104. data/test/storage/local_test.rb +54 -0
  105. data/test/storage/redis_test.rb +75 -0
  106. data/test/storage/sql_test.rb +55 -0
  107. data/test/storage/storage_tests.rb +134 -0
  108. data/test/storage_test.rb +90 -0
  109. data/test/stream/client/auth_test.rb +127 -0
  110. data/test/stream/client/ready_test.rb +47 -0
  111. data/test/stream/component/handshake_test.rb +46 -0
  112. data/test/stream/component/ready_test.rb +105 -0
  113. data/test/stream/component/start_test.rb +41 -0
  114. data/test/stream/parser_test.rb +121 -0
  115. data/test/stream/server/outbound/auth_test.rb +77 -0
  116. data/test/stream/server/ready_test.rb +100 -0
  117. data/test/token_bucket_test.rb +24 -0
  118. data/test/user_test.rb +64 -0
  119. metadata +318 -0
@@ -0,0 +1,5 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+
5
+ # The main starting point for the XMPP server process. Starts the
6
+ # EventMachine processing loop and registers the XMPP protocol handler
7
+ # with the ports defined in the server configuration file.
8
+ class XmppServer
9
+ include Vines::Log
10
+
11
+ def initialize(config)
12
+ @config = config
13
+ end
14
+
15
+ def start
16
+ log.info('XMPP server started')
17
+ at_exit { log.fatal('XMPP server stopped') }
18
+ EM.epoll
19
+ EM.kqueue
20
+ EM.run do
21
+ @config.ports.each {|port| port.start }
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,396 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'test/unit'
5
+
6
+ class ConfigTest < Test::Unit::TestCase
7
+ def test_missing_host
8
+ assert_raise(RuntimeError) do
9
+ Vines::Config.new do
10
+ # missing hosts
11
+ end
12
+ end
13
+ end
14
+
15
+ def test_duplicate_host
16
+ assert_raise(RuntimeError) do
17
+ Vines::Config.new do
18
+ host 'wonderland.lit' do
19
+ storage 'fs' do
20
+ dir '.'
21
+ end
22
+ end
23
+ host 'wonderland.lit' do
24
+ storage 'fs' do
25
+ dir '.'
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ def test_duplicate_host_in_one_call
33
+ assert_raise(RuntimeError) do
34
+ Vines::Config.new do
35
+ host 'wonderland.lit', 'wonderland.lit' do
36
+ storage 'fs' do
37
+ dir '.'
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ def test_missing_storage
45
+ assert_raise(RuntimeError) do
46
+ Vines::Config.new do
47
+ host 'wonderland.lit' do
48
+ # missing storage
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def test_bad_storage
55
+ assert_raise(RuntimeError) do
56
+ Vines::Config.new do
57
+ host 'wonderland.lit' do
58
+ storage 'bogus' do
59
+ # no bogus storage implementation
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def test_duplicate_storage
67
+ assert_raise(RuntimeError) do
68
+ Vines::Config.new do
69
+ host 'wonderland.lit' do
70
+ storage('fs') { dir '.' }
71
+ storage('fs') { dir '.' }
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ def test_good_storage
78
+ assert_nothing_raised do
79
+ Vines::Config.new do
80
+ host 'wonderland.lit' do
81
+ storage 'fs' do
82
+ dir '.'
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ def test_ldap_added_to_storage
90
+ config = Vines::Config.new do
91
+ host 'wonderland.lit' do
92
+ storage(:fs) { dir '.' }
93
+ # added after storage
94
+ ldap 'ldap.wonderland.lit', 1636 do
95
+ tls true
96
+ dn 'cn=Directory Manager'
97
+ password 'secr3t'
98
+ basedn 'dc=wonderland,dc=lit'
99
+ object_class 'person'
100
+ user_attr 'uid'
101
+ name_attr 'cn'
102
+ end
103
+ end
104
+
105
+ host 'verona.lit' do
106
+ ldap 'ldap.verona.lit', 1636 do
107
+ tls true
108
+ dn 'cn=Directory Manager'
109
+ password 'secr3t'
110
+ basedn 'dc=wonderland,dc=lit'
111
+ object_class 'person'
112
+ user_attr 'uid'
113
+ name_attr 'cn'
114
+ end
115
+ # added before storage
116
+ storage(:fs) { dir '.' }
117
+ end
118
+ end
119
+ %w[wonderland.lit verona.lit].each do |domain|
120
+ assert_not_nil config.vhosts[domain].ldap
121
+ assert config.vhosts[domain].ldap?
122
+ end
123
+ end
124
+
125
+ def test_configure
126
+ config = Vines::Config.configure do
127
+ host 'wonderland.lit' do
128
+ storage :fs do
129
+ dir '.'
130
+ end
131
+ end
132
+ end
133
+ assert_not_nil config
134
+ assert_same config, Vines::Config.instance
135
+ end
136
+
137
+ def test_vhost
138
+ config = Vines::Config.new do
139
+ host 'wonderland.lit' do
140
+ storage(:fs) { dir '.' }
141
+ end
142
+ end
143
+ assert_equal ['wonderland.lit'], config.vhosts.keys
144
+ assert config.vhost?('wonderland.lit')
145
+ assert !config.vhost?('bogus')
146
+ end
147
+
148
+ def test_port_lookup
149
+ config = Vines::Config.new do
150
+ host 'wonderland.lit' do
151
+ storage(:fs) { dir '.' }
152
+ end
153
+ client
154
+ end
155
+ assert_not_nil config[:client]
156
+ assert_raise(ArgumentError) { config[:server] }
157
+ assert_raise(ArgumentError) { config[:bogus] }
158
+ end
159
+
160
+ def test_duplicate_client
161
+ assert_raise(RuntimeError) do
162
+ Vines::Config.new do
163
+ client
164
+ client
165
+ end
166
+ end
167
+ end
168
+
169
+ def test_duplicate_server
170
+ assert_raise(RuntimeError) do
171
+ Vines::Config.new do
172
+ server
173
+ server
174
+ end
175
+ end
176
+ end
177
+
178
+ def test_duplicate_http
179
+ assert_raise(RuntimeError) do
180
+ Vines::Config.new do
181
+ http
182
+ http
183
+ end
184
+ end
185
+ end
186
+
187
+ def test_duplicate_component
188
+ assert_raise(RuntimeError) do
189
+ Vines::Config.new do
190
+ component
191
+ component
192
+ end
193
+ end
194
+ end
195
+
196
+ def test_default_client
197
+ config = Vines::Config.new do
198
+ host 'wonderland.lit' do
199
+ storage(:fs) { dir '.' }
200
+ end
201
+ client
202
+ end
203
+ port = config.ports.first
204
+ assert_not_nil port
205
+ assert_equal Vines::Config::ClientPort, port.class
206
+ assert_equal '0.0.0.0', port.host
207
+ assert_equal 5222, port.port
208
+ assert_equal 131_072, port.max_stanza_size
209
+ assert_equal 5, port.max_resources_per_account
210
+ assert_equal Vines::Stream::Client, port.stream
211
+ assert_same config, port.config
212
+ assert_equal 1, config.ports.size
213
+ end
214
+
215
+ def test_configured_client
216
+ config = Vines::Config.new do
217
+ host 'wonderland.lit' do
218
+ storage(:fs) { dir '.' }
219
+ end
220
+ client '0.0.0.1', 42 do
221
+ max_stanza_size 60_000
222
+ max_resources_per_account 1
223
+ end
224
+ end
225
+ port = config.ports.first
226
+ assert_not_nil port
227
+ assert_equal Vines::Config::ClientPort, port.class
228
+ assert_equal '0.0.0.1', port.host
229
+ assert_equal 42, port.port
230
+ assert_equal 60_000, port.max_stanza_size
231
+ assert_equal 1, port.max_resources_per_account
232
+ assert_equal Vines::Stream::Client, port.stream
233
+ assert_same config, port.config
234
+ assert_equal 1, config.ports.size
235
+ end
236
+
237
+ def test_max_stanza_size
238
+ config = Vines::Config.new do
239
+ host 'wonderland.lit' do
240
+ storage(:fs) { dir '.' }
241
+ end
242
+ client do
243
+ max_stanza_size 0
244
+ end
245
+ end
246
+ assert_equal 10_000, config.ports.first.max_stanza_size
247
+ end
248
+
249
+ def test_default_server
250
+ config = Vines::Config.new do
251
+ host 'wonderland.lit' do
252
+ storage(:fs) { dir '.' }
253
+ end
254
+ server
255
+ end
256
+ port = config.ports.first
257
+ assert_not_nil port
258
+ assert !config.s2s?('verona.lit')
259
+ assert_equal Vines::Config::ServerPort, port.class
260
+ assert_equal '0.0.0.0', port.host
261
+ assert_equal 5269, port.port
262
+ assert_equal 131_072, port.max_stanza_size
263
+ assert_equal Vines::Stream::Server, port.stream
264
+ assert_same config, port.config
265
+ assert_equal 1, config.ports.size
266
+ end
267
+
268
+ def test_configured_server
269
+ config = Vines::Config.new do
270
+ host 'wonderland.lit' do
271
+ storage(:fs) { dir '.' }
272
+ end
273
+ server '0.0.0.1', 42 do
274
+ max_stanza_size 60_000
275
+ hosts ['verona.lit', 'denmark.lit']
276
+ end
277
+ end
278
+ port = config.ports.first
279
+ assert_not_nil port
280
+ assert config.s2s?('verona.lit')
281
+ assert config.s2s?('denmark.lit')
282
+ assert !config.s2s?('bogus')
283
+ assert_equal Vines::Config::ServerPort, port.class
284
+ assert_equal '0.0.0.1', port.host
285
+ assert_equal 42, port.port
286
+ assert_equal 60_000, port.max_stanza_size
287
+ assert_equal Vines::Stream::Server, port.stream
288
+ assert_same config, port.config
289
+ assert_equal 1, config.ports.size
290
+ end
291
+
292
+ def test_default_http
293
+ config = Vines::Config.new do
294
+ host 'wonderland.lit' do
295
+ storage(:fs) { dir '.' }
296
+ end
297
+ http
298
+ end
299
+ port = config.ports.first
300
+ assert_not_nil port
301
+ assert_equal Vines::Config::HttpPort, port.class
302
+ assert_equal '0.0.0.0', port.host
303
+ assert_equal 5280, port.port
304
+ assert_equal 131_072, port.max_stanza_size
305
+ assert_equal Vines::Stream::Http, port.stream
306
+ assert_same config, port.config
307
+ assert_equal 1, config.ports.size
308
+ end
309
+
310
+ def test_configured_http
311
+ config = Vines::Config.new do
312
+ host 'wonderland.lit' do
313
+ storage(:fs) { dir '.' }
314
+ end
315
+ http '0.0.0.1', 42 do
316
+ max_stanza_size 60_000
317
+ end
318
+ end
319
+ port = config.ports.first
320
+ assert_not_nil port
321
+ assert_equal Vines::Config::HttpPort, port.class
322
+ assert_equal '0.0.0.1', port.host
323
+ assert_equal 42, port.port
324
+ assert_equal 60_000, port.max_stanza_size
325
+ assert_equal Vines::Stream::Http, port.stream
326
+ assert_same config, port.config
327
+ assert_equal 1, config.ports.size
328
+ end
329
+
330
+ def test_default_component
331
+ config = Vines::Config.new do
332
+ host 'wonderland.lit' do
333
+ storage(:fs) { dir '.' }
334
+ end
335
+ component
336
+ end
337
+ port = config.ports.first
338
+ assert_not_nil port
339
+ assert port.components.empty?
340
+ assert_nil port.password('bogus')
341
+ assert_equal Vines::Config::ComponentPort, port.class
342
+ assert_equal '0.0.0.0', port.host
343
+ assert_equal 5347, port.port
344
+ assert_equal 131_072, port.max_stanza_size
345
+ assert_equal Vines::Stream::Component, port.stream
346
+ assert_same config, port.config
347
+ assert_equal 1, config.ports.size
348
+ end
349
+
350
+ def test_configured_component
351
+ config = Vines::Config.new do
352
+ host 'wonderland.lit' do
353
+ storage(:fs) { dir '.' }
354
+ end
355
+ component '0.0.0.1', 42 do
356
+ max_stanza_size 60_000
357
+ components 'tea.wonderland.lit' => 'secr3t',
358
+ 'cake.wonderland.lit' => 'passw0rd'
359
+ end
360
+ end
361
+ port = config.ports.first
362
+ assert_not_nil port
363
+ assert_equal 2, port.components.size
364
+ assert_equal 'secr3t', port.password('tea.wonderland.lit')
365
+ assert_equal 'passw0rd', port.password('cake.wonderland.lit')
366
+ assert_nil port.password('bogus')
367
+ assert_equal Vines::Config::ComponentPort, port.class
368
+ assert_equal '0.0.0.1', port.host
369
+ assert_equal 42, port.port
370
+ assert_equal 60_000, port.max_stanza_size
371
+ assert_equal Vines::Stream::Component, port.stream
372
+ assert_same config, port.config
373
+ assert_equal 1, config.ports.size
374
+ end
375
+
376
+ def test_invalid_log_level
377
+ assert_raise(RuntimeError) do
378
+ config = Vines::Config.new do
379
+ log 'bogus'
380
+ host 'wonderland.lit' do
381
+ storage(:fs) { dir '.' }
382
+ end
383
+ end
384
+ end
385
+ end
386
+
387
+ def test_valid_log_level
388
+ config = Vines::Config.new do
389
+ log :error
390
+ host 'wonderland.lit' do
391
+ storage(:fs) { dir '.' }
392
+ end
393
+ end
394
+ assert_equal Logger::ERROR, Class.new.extend(Vines::Log).log.level
395
+ end
396
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'vines'
4
+ require 'test/unit'
5
+
6
+ class ErrorTest < Test::Unit::TestCase
7
+ def test_sasl_error_without_text
8
+ expected = %q{<failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><temporary-auth-failure/></failure>}
9
+ assert_equal expected, Vines::SaslErrors::TemporaryAuthFailure.new.to_xml
10
+ end
11
+
12
+ def test_sasl_error_with_text
13
+ text = %q{<text xml:lang="en">busted</text>}
14
+ expected = %q{<failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><temporary-auth-failure/>%s</failure>} % text
15
+ assert_equal expected, Vines::SaslErrors::TemporaryAuthFailure.new('busted').to_xml
16
+ end
17
+
18
+ def test_stream_error_without_text
19
+ expected = %q{<stream:error><internal-server-error xmlns="urn:ietf:params:xml:ns:xmpp-streams"/></stream:error>}
20
+ assert_equal expected, Vines::StreamErrors::InternalServerError.new.to_xml
21
+ end
22
+
23
+ def test_stream_error_with_text
24
+ text = %q{<text xmlns="urn:ietf:params:xml:ns:xmpp-streams" xml:lang="en">busted</text>}
25
+ expected = %q{<stream:error><internal-server-error xmlns="urn:ietf:params:xml:ns:xmpp-streams"/>%s</stream:error>} % text
26
+ assert_equal expected, Vines::StreamErrors::InternalServerError.new('busted').to_xml
27
+ end
28
+
29
+ def test_stanza_error_with_bad_type
30
+ node = node('<message/>')
31
+ assert_raises(RuntimeError) { Vines::StanzaErrors::BadRequest.new(node, 'bogus') }
32
+ end
33
+
34
+ def test_stanza_error_with_bad_stanza
35
+ node = node('<bogus/>')
36
+ assert_raises(RuntimeError) { Vines::StanzaErrors::BadRequest.new(node, 'modify') }
37
+ end
38
+
39
+ def test_stanza_error_without_text
40
+ error = %q{<error type="modify"><bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error>}
41
+ expected = %q{<message from="hatter@wonderland.lit" to="alice@wonderland.lit" type="error">%s</message>} % error
42
+ node = node(%Q{<message from="alice@wonderland.lit" to="hatter@wonderland.lit"/>})
43
+ assert_equal expected, Vines::StanzaErrors::BadRequest.new(node, 'modify').to_xml
44
+ end
45
+
46
+ def test_stanza_error_with_text
47
+ text = %q{<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" xml:lang="en">busted</text>}
48
+ error = %q{<error type="modify"><bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>%s</error>} % text
49
+ expected = %q{<message id="42" type="error">%s</message>} % error
50
+ node = node(%Q{<message id="42"/>})
51
+ assert_equal expected, Vines::StanzaErrors::BadRequest.new(node, 'modify', 'busted').to_xml
52
+ end
53
+
54
+ private
55
+
56
+ def node(xml)
57
+ Nokogiri::XML(xml).root
58
+ end
59
+ end