vines 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +1 -1
- data/Rakefile +33 -53
- data/conf/certs/README +10 -3
- data/conf/certs/ca-bundle.crt +55 -410
- data/lib/vines/cluster/connection.rb +1 -1
- data/lib/vines/command/ldap.rb +1 -1
- data/lib/vines/command/schema.rb +1 -1
- data/lib/vines/config.rb +11 -5
- data/lib/vines/jid.rb +3 -3
- data/lib/vines/stanza/iq/disco_items.rb +1 -1
- data/lib/vines/storage/local.rb +1 -0
- data/lib/vines/store.rb +50 -9
- data/lib/vines/stream.rb +5 -9
- data/lib/vines/stream/client/session.rb +1 -1
- data/lib/vines/stream/http.rb +3 -1
- data/lib/vines/stream/http/ready.rb +5 -1
- data/lib/vines/stream/http/request.rb +22 -0
- data/lib/vines/version.rb +1 -1
- data/test/config/host_test.rb +9 -9
- data/test/config/pubsub_test.rb +2 -2
- data/test/config_test.rb +5 -3
- data/test/jid_test.rb +9 -0
- data/test/rake_test_loader.rb +1 -1
- data/test/router_test.rb +7 -7
- data/test/stanza/iq/disco_info_test.rb +2 -2
- data/test/stanza/iq/private_storage_test.rb +1 -1
- data/test/stanza/iq_test.rb +1 -1
- data/test/stanza/presence/subscribe_test.rb +1 -1
- data/test/stanza/pubsub/subscribe_test.rb +3 -3
- data/test/stanza/pubsub/unsubscribe_test.rb +3 -3
- data/test/storage_test.rb +15 -8
- data/test/store_test.rb +131 -0
- data/test/stream/client/ready_test.rb +6 -6
- data/test/stream/component/ready_test.rb +1 -1
- data/test/stream/http/ready_test.rb +46 -11
- data/test/stream/http/request_test.rb +83 -11
- data/test/stream/http/sessions_test.rb +2 -2
- data/web/chat/coffeescripts/chat.coffee +1 -1
- data/web/chat/index.html +9 -10
- data/web/chat/javascripts/app.js +1 -1
- data/web/lib/coffeescripts/button.coffee +1 -1
- data/web/lib/coffeescripts/contact.coffee +2 -2
- data/web/lib/coffeescripts/filter.coffee +1 -1
- data/web/lib/coffeescripts/layout.coffee +2 -2
- data/web/lib/coffeescripts/login.coffee +1 -1
- data/web/lib/coffeescripts/logout.coffee +2 -2
- data/web/lib/coffeescripts/navbar.coffee +1 -1
- data/web/lib/coffeescripts/notification.coffee +1 -1
- data/web/lib/coffeescripts/router.coffee +1 -1
- data/web/lib/coffeescripts/session.coffee +1 -1
- data/web/lib/coffeescripts/transfer.coffee +1 -1
- data/web/lib/javascripts/base.js +10 -9
- metadata +65 -43
- data/web/chat/javascripts/chat.js +0 -390
- data/web/chat/javascripts/init.js +0 -21
- data/web/lib/javascripts/button.js +0 -39
- data/web/lib/javascripts/contact.js +0 -94
- data/web/lib/javascripts/filter.js +0 -88
- data/web/lib/javascripts/layout.js +0 -48
- data/web/lib/javascripts/login.js +0 -88
- data/web/lib/javascripts/logout.js +0 -11
- data/web/lib/javascripts/navbar.js +0 -69
- data/web/lib/javascripts/notification.js +0 -26
- data/web/lib/javascripts/router.js +0 -105
- data/web/lib/javascripts/session.js +0 -291
- data/web/lib/javascripts/transfer.js +0 -124
data/lib/vines/command/ldap.rb
CHANGED
@@ -7,7 +7,7 @@ module Vines
|
|
7
7
|
raise 'vines ldap <domain>' unless opts[:args].size == 1
|
8
8
|
require opts[:config]
|
9
9
|
domain = opts[:args].first
|
10
|
-
unless storage = Config.instance.
|
10
|
+
unless storage = Config.instance.vhost(domain).storage rescue nil
|
11
11
|
raise "#{domain} virtual host not found in conf/config.rb"
|
12
12
|
end
|
13
13
|
unless storage.ldap?
|
data/lib/vines/command/schema.rb
CHANGED
@@ -7,7 +7,7 @@ module Vines
|
|
7
7
|
raise 'vines schema <domain>' unless opts[:args].size == 1
|
8
8
|
require opts[:config]
|
9
9
|
domain = opts[:args].first
|
10
|
-
unless storage = Config.instance.
|
10
|
+
unless storage = Config.instance.vhost(domain).storage rescue nil
|
11
11
|
raise "#{domain} virtual host not found in conf/config.rb"
|
12
12
|
end
|
13
13
|
unless storage.respond_to?(:create_schema)
|
data/lib/vines/config.rb
CHANGED
@@ -9,7 +9,7 @@ module Vines
|
|
9
9
|
class Config
|
10
10
|
LOG_LEVELS = %w[debug info warn error fatal].freeze
|
11
11
|
|
12
|
-
attr_reader :router
|
12
|
+
attr_reader :router
|
13
13
|
|
14
14
|
@@instance = nil
|
15
15
|
def self.configure(&block)
|
@@ -67,13 +67,19 @@ module Vines
|
|
67
67
|
|
68
68
|
# Return true if the domain is virtual hosted by this server.
|
69
69
|
def vhost?(domain)
|
70
|
-
|
70
|
+
!!vhost(domain)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Return the Host config object for this domain if it's hosted by this
|
74
|
+
# server.
|
75
|
+
def vhost(domain)
|
76
|
+
@vhosts[domain.to_s]
|
71
77
|
end
|
72
78
|
|
73
79
|
# Returns the storage system for the domain or a Storage::Null instance if
|
74
80
|
# the domain is not hosted at this server.
|
75
81
|
def storage(domain)
|
76
|
-
host =
|
82
|
+
host = vhost(domain)
|
77
83
|
host ? host.storage : @null
|
78
84
|
end
|
79
85
|
|
@@ -112,7 +118,7 @@ module Vines
|
|
112
118
|
|
113
119
|
# Return true if private XML fragment storage is enabled for this domain.
|
114
120
|
def private_storage?(domain)
|
115
|
-
host =
|
121
|
+
host = vhost(domain)
|
116
122
|
host.private_storage? if host
|
117
123
|
end
|
118
124
|
|
@@ -191,7 +197,7 @@ module Vines
|
|
191
197
|
# Return true if all JIDs are allowed to exchange cross domain messages.
|
192
198
|
def cross_domain?(*jids)
|
193
199
|
!jids.flatten.index do |jid|
|
194
|
-
|
200
|
+
!vhost(jid.domain).cross_domain_messages?
|
195
201
|
end
|
196
202
|
end
|
197
203
|
end
|
data/lib/vines/jid.rb
CHANGED
@@ -4,13 +4,13 @@ module Vines
|
|
4
4
|
class JID
|
5
5
|
include Comparable
|
6
6
|
|
7
|
-
PATTERN =
|
7
|
+
PATTERN = /\A(?:([^@]*)@)??([^@\/]*)(?:\/(.*?))?\Z/.freeze
|
8
8
|
|
9
9
|
# http://tools.ietf.org/html/rfc6122#appendix-A
|
10
|
-
NODE_PREP = /[[:
|
10
|
+
NODE_PREP = /[[:cntrl:] "&'\/:<>@]/.freeze
|
11
11
|
|
12
12
|
# http://tools.ietf.org/html/rfc3454#appendix-C
|
13
|
-
NAME_PREP = /[[:
|
13
|
+
NAME_PREP = /[[:cntrl:] ]/.freeze
|
14
14
|
|
15
15
|
attr_reader :node, :domain, :resource
|
16
16
|
attr_writer :resource
|
@@ -15,7 +15,7 @@ module Vines
|
|
15
15
|
query.default_namespace = NS
|
16
16
|
unless to_pubsub_domain?
|
17
17
|
to = (validate_to || stream.domain).to_s
|
18
|
-
stream.config.
|
18
|
+
stream.config.vhost(to).disco_items.each do |domain|
|
19
19
|
query << el.document.create_element('item', 'jid' => domain)
|
20
20
|
end
|
21
21
|
end
|
data/lib/vines/storage/local.rb
CHANGED
data/lib/vines/store.rb
CHANGED
@@ -6,9 +6,12 @@ module Vines
|
|
6
6
|
# This uses the conf/certs/*.crt files as the list of trusted root
|
7
7
|
# CA certificates.
|
8
8
|
class Store
|
9
|
-
@@
|
9
|
+
@@sources = nil
|
10
10
|
|
11
|
-
|
11
|
+
# Create a certificate store to read certificate files from the given
|
12
|
+
# directory.
|
13
|
+
def initialize(dir)
|
14
|
+
@dir = File.expand_path(dir)
|
12
15
|
@store = OpenSSL::X509::Store.new
|
13
16
|
certs.each {|c| @store.add_cert(c) }
|
14
17
|
end
|
@@ -37,15 +40,53 @@ module Vines
|
|
37
40
|
# certificates are used to start the trust chain needed to validate certs
|
38
41
|
# we receive from clients and servers.
|
39
42
|
def certs
|
40
|
-
unless @@
|
43
|
+
unless @@sources
|
41
44
|
pattern = /-{5}BEGIN CERTIFICATE-{5}\n.*?-{5}END CERTIFICATE-{5}\n/m
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
pairs = Dir[File.join(@dir, '*.crt')].map do |name|
|
46
|
+
pems = File.read(name).scan(pattern)
|
47
|
+
certs = pems.map {|pem| OpenSSL::X509::Certificate.new(pem) }
|
48
|
+
certs.reject! {|cert| cert.not_after < Time.now }
|
49
|
+
[name, certs]
|
50
|
+
end
|
51
|
+
@@sources = Hash[pairs]
|
52
|
+
end
|
53
|
+
@@sources.values.flatten
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns a pair of file names containing the public key certificate
|
57
|
+
# and matching private key for the given domain. This supports using
|
58
|
+
# wildcard certificate files to serve several subdomains.
|
59
|
+
#
|
60
|
+
# Finding the certificate and private key file for a domain follows these steps:
|
61
|
+
# - look for <domain>.crt and <domain>.key files in the conf/certs directory.
|
62
|
+
# if found, return those file names, else
|
63
|
+
# - inspect all conf/certs/*.crt files for certificates that contain the
|
64
|
+
# domain name either as the subject common name (CN) or as a DNS
|
65
|
+
# subjectAltName. The corresponding private key must be in a file of the
|
66
|
+
# same name as the certificate's, but with a .key extension.
|
67
|
+
#
|
68
|
+
# So in the simplest configuration, the tea.wonderland.lit encryption files would
|
69
|
+
# be named conf/certs/tea.wonderland.lit.crt and conf/certs/tea.wonderland.lit.key.
|
70
|
+
#
|
71
|
+
# However, in the case of a wildcard certificate for *.wonderland.lit, the
|
72
|
+
# files would be conf/certs/wonderland.lit.crt and conf/certs/wonderland.lit.key.
|
73
|
+
# These same two files would be returned for the subdomains of tea.wonderland.lit,
|
74
|
+
# crumpets.wonderland.lit, etc.
|
75
|
+
def files_for_domain(domain)
|
76
|
+
crt = File.expand_path("#{domain}.crt", @dir)
|
77
|
+
key = File.expand_path("#{domain}.key", @dir)
|
78
|
+
return [crt, key] if File.exists?(crt) && File.exists?(key)
|
79
|
+
|
80
|
+
# might be a wildcard cert file
|
81
|
+
@@sources.each do |file, certs|
|
82
|
+
certs.each do |cert|
|
83
|
+
if OpenSSL::SSL.verify_certificate_identity(cert, domain)
|
84
|
+
key = file.chomp(File.extname(file)) + '.key'
|
85
|
+
return [file, key] if File.exists?(file) && File.exists?(key)
|
86
|
+
end
|
87
|
+
end
|
47
88
|
end
|
48
|
-
|
89
|
+
nil
|
49
90
|
end
|
50
91
|
end
|
51
92
|
end
|
data/lib/vines/stream.rb
CHANGED
@@ -21,7 +21,7 @@ module Vines
|
|
21
21
|
@remote_addr, @local_addr = addresses
|
22
22
|
@user, @closed, @stanza_size = nil, false, 0
|
23
23
|
@bucket = TokenBucket.new(100, 10)
|
24
|
-
@store = Store.new
|
24
|
+
@store = Store.new(File.join(VINES_ROOT, 'conf', 'certs'))
|
25
25
|
@nodes = EM::Queue.new
|
26
26
|
process_node_queue
|
27
27
|
create_parser
|
@@ -71,7 +71,7 @@ module Vines
|
|
71
71
|
|
72
72
|
# Returns the Vines::Config::Host virtual host for the stream's domain.
|
73
73
|
def vhost
|
74
|
-
@config.
|
74
|
+
@config.vhost(domain)
|
75
75
|
end
|
76
76
|
|
77
77
|
# Reload the user's information into their active connections. Call this
|
@@ -118,14 +118,14 @@ module Vines
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def encrypt
|
121
|
-
cert, key =
|
122
|
-
start_tls(:
|
121
|
+
cert, key = @store.files_for_domain(domain)
|
122
|
+
start_tls(cert_chain_file: cert, private_key_file: key, verify_peer: true)
|
123
123
|
end
|
124
124
|
|
125
125
|
# Returns true if the TLS certificate and private key files for this domain
|
126
126
|
# exist and can be used to encrypt this stream.
|
127
127
|
def encrypt?
|
128
|
-
|
128
|
+
!@store.files_for_domain(domain).nil?
|
129
129
|
end
|
130
130
|
|
131
131
|
def unbind
|
@@ -235,10 +235,6 @@ module Vines
|
|
235
235
|
@state
|
236
236
|
end
|
237
237
|
|
238
|
-
def tls_files
|
239
|
-
%w[crt key].map {|ext| File.join(VINES_ROOT, 'conf', 'certs', "#{domain}.#{ext}") }
|
240
|
-
end
|
241
|
-
|
242
238
|
# Return true if this is a valid domain-only JID that can be used in
|
243
239
|
# stream initiation stanza headers.
|
244
240
|
def valid_address?(jid)
|
data/lib/vines/stream/http.rb
CHANGED
@@ -40,7 +40,9 @@ module Vines
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def process_request(request)
|
43
|
-
if request.path == self.bind
|
43
|
+
if request.path == self.bind && request.options?
|
44
|
+
request.reply_to_options
|
45
|
+
elsif request.path == self.bind
|
44
46
|
body = Nokogiri::XML(request.body).root
|
45
47
|
if session = Sessions[body['sid']]
|
46
48
|
@session = session
|
@@ -11,6 +11,7 @@ module Vines
|
|
11
11
|
NOT_MODIFIED = 'Not Modified'.freeze
|
12
12
|
IF_MODIFIED = 'If-Modified-Since'.freeze
|
13
13
|
TEXT_PLAIN = 'text/plain'.freeze
|
14
|
+
OPTIONS = 'OPTIONS'.freeze
|
14
15
|
CONTENT_TYPES = {
|
15
16
|
'html' => 'text/html; charset="utf-8"',
|
16
17
|
'js' => 'application/javascript; charset="utf-8"',
|
@@ -72,12 +73,33 @@ module Vines
|
|
72
73
|
body = node.to_s
|
73
74
|
header = [
|
74
75
|
"HTTP/1.1 200 OK",
|
76
|
+
"Access-Control-Allow-Origin: *",
|
75
77
|
"Content-Type: #{content_type}",
|
76
78
|
"Content-Length: #{body.bytesize}"
|
77
79
|
].join("\r\n")
|
78
80
|
@stream.stream_write([header, body].join("\r\n\r\n"))
|
79
81
|
end
|
80
82
|
|
83
|
+
# Return true if the request method is OPTIONS, signaling a
|
84
|
+
# CORS preflight check.
|
85
|
+
def options?
|
86
|
+
@method == OPTIONS
|
87
|
+
end
|
88
|
+
|
89
|
+
# Send a 200 OK response, allowing any origin domain to connect to the
|
90
|
+
# server, in response to CORS preflight OPTIONS requests. This allows
|
91
|
+
# any web application using strophe.js to connect to our BOSH port.
|
92
|
+
def reply_to_options
|
93
|
+
allow = @headers['Access-Control-Request-Headers']
|
94
|
+
headers = [
|
95
|
+
"Access-Control-Allow-Origin: *",
|
96
|
+
"Access-Control-Allow-Methods: POST, GET, OPTIONS",
|
97
|
+
"Access-Control-Allow-Headers: #{allow}",
|
98
|
+
"Access-Control-Max-Age: #{60 * 60 * 24 * 30}"
|
99
|
+
]
|
100
|
+
send_status(200, 'OK', headers)
|
101
|
+
end
|
102
|
+
|
81
103
|
private
|
82
104
|
|
83
105
|
# Attempt to rebuild the full request URI from the Host header. If it
|
data/lib/vines/version.rb
CHANGED
data/test/config/host_test.rb
CHANGED
@@ -46,7 +46,7 @@ class HostTest < MiniTest::Unit::TestCase
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
49
|
-
refute_nil config.
|
49
|
+
refute_nil config.vhost('wonderland.lit').storage
|
50
50
|
end
|
51
51
|
|
52
52
|
def test_ldap_added_to_storage
|
@@ -81,8 +81,8 @@ class HostTest < MiniTest::Unit::TestCase
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
%w[wonderland.lit verona.lit].each do |domain|
|
84
|
-
refute_nil config.
|
85
|
-
assert config.
|
84
|
+
refute_nil config.vhost(domain).storage.ldap
|
85
|
+
assert config.vhost(domain).storage.ldap?
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
@@ -225,7 +225,7 @@ class HostTest < MiniTest::Unit::TestCase
|
|
225
225
|
components 'TEA' => 'secr3t', CAKE: 'Passw0rd'
|
226
226
|
end
|
227
227
|
end
|
228
|
-
host = config.
|
228
|
+
host = config.vhost('wonderland.lit')
|
229
229
|
refute_nil host
|
230
230
|
assert_equal 2, host.components.size
|
231
231
|
assert_equal host.components['tea.wonderland.lit'], 'secr3t'
|
@@ -239,7 +239,7 @@ class HostTest < MiniTest::Unit::TestCase
|
|
239
239
|
components 'tea' => 'secr3t', cake: 'passw0rd'
|
240
240
|
end
|
241
241
|
end
|
242
|
-
host = config.
|
242
|
+
host = config.vhost('wonderland.lit')
|
243
243
|
refute_nil host
|
244
244
|
refute host.component?(nil)
|
245
245
|
refute host.component?('tea')
|
@@ -309,7 +309,7 @@ class HostTest < MiniTest::Unit::TestCase
|
|
309
309
|
pubsub 'TEA', :CAKE
|
310
310
|
end
|
311
311
|
end
|
312
|
-
host = config.
|
312
|
+
host = config.vhost('wonderland.lit')
|
313
313
|
refute_nil host
|
314
314
|
assert_equal 2, host.pubsubs.size
|
315
315
|
refute_nil host.pubsubs['tea.wonderland.lit']
|
@@ -323,7 +323,7 @@ class HostTest < MiniTest::Unit::TestCase
|
|
323
323
|
pubsub 'tea', :cake
|
324
324
|
end
|
325
325
|
end
|
326
|
-
host = config.
|
326
|
+
host = config.vhost('wonderland.lit')
|
327
327
|
refute_nil host
|
328
328
|
refute host.pubsub?(nil)
|
329
329
|
refute host.pubsub?('tea')
|
@@ -348,7 +348,7 @@ class HostTest < MiniTest::Unit::TestCase
|
|
348
348
|
storage(:fs) { dir Dir.tmpdir }
|
349
349
|
end
|
350
350
|
end
|
351
|
-
host = config.
|
351
|
+
host = config.vhost('wonderland.lit')
|
352
352
|
refute_nil host
|
353
353
|
refute host.private_storage?
|
354
354
|
end
|
@@ -360,7 +360,7 @@ class HostTest < MiniTest::Unit::TestCase
|
|
360
360
|
storage(:fs) { dir Dir.tmpdir }
|
361
361
|
end
|
362
362
|
end
|
363
|
-
host = config.
|
363
|
+
host = config.vhost('wonderland.lit')
|
364
364
|
refute_nil host
|
365
365
|
assert host.private_storage?
|
366
366
|
assert config.private_storage?('wonderland.lit')
|
data/test/config/pubsub_test.rb
CHANGED
@@ -52,7 +52,7 @@ class ConfigPubSubTest < MiniTest::Unit::TestCase
|
|
52
52
|
def test_subscribe_remote_jid_is_allowed
|
53
53
|
topic = 'remote_jids_allowed'
|
54
54
|
jid = 'romeo@verona.lit'
|
55
|
-
@config.
|
55
|
+
@config.vhost('wonderland.lit').cross_domain_messages true
|
56
56
|
@pubsub.add_node(topic)
|
57
57
|
@pubsub.subscribe(topic, jid)
|
58
58
|
assert @pubsub.subscribed?(topic, jid)
|
@@ -105,7 +105,7 @@ class ConfigPubSubTest < MiniTest::Unit::TestCase
|
|
105
105
|
alice = Vines::JID.new('alice@wonderland.lit')
|
106
106
|
romeo = Vines::JID.new('romeo@verona.lit')
|
107
107
|
|
108
|
-
@config.
|
108
|
+
@config.vhost('wonderland.lit').cross_domain_messages true
|
109
109
|
def @config.router
|
110
110
|
unless @mock_router
|
111
111
|
@mock_router = MiniTest::Mock.new
|
data/test/config_test.rb
CHANGED
@@ -60,8 +60,10 @@ class ConfigTest < MiniTest::Unit::TestCase
|
|
60
60
|
storage(:fs) { dir Dir.tmpdir }
|
61
61
|
end
|
62
62
|
end
|
63
|
-
|
63
|
+
refute_nil config.vhost('wonderland.lit')
|
64
|
+
refute_nil config.vhost(Vines::JID.new('wonderland.lit'))
|
64
65
|
assert config.vhost?('wonderland.lit')
|
66
|
+
assert config.vhost?(Vines::JID.new('wonderland.lit'))
|
65
67
|
refute config.vhost?('alice@wonderland.lit')
|
66
68
|
refute config.vhost?('tea.wonderland.lit')
|
67
69
|
refute config.vhost?('bogus')
|
@@ -394,8 +396,8 @@ class ConfigTest < MiniTest::Unit::TestCase
|
|
394
396
|
storage(:fs) { dir Dir.tmpdir }
|
395
397
|
end
|
396
398
|
end
|
397
|
-
refute config.
|
398
|
-
assert config.
|
399
|
+
refute config.vhost('wonderland.lit').cross_domain_messages?
|
400
|
+
assert config.vhost('verona.lit').cross_domain_messages?
|
399
401
|
end
|
400
402
|
|
401
403
|
def test_local_jid?
|
data/test/jid_test.rb
CHANGED
@@ -115,10 +115,19 @@ class JidTest < MiniTest::Unit::TestCase
|
|
115
115
|
assert_raises(ArgumentError) { Vines::JID.new(%q{alice>s@wonderland.lit}) }
|
116
116
|
assert_raises(ArgumentError) { Vines::JID.new("alice\u0000s@wonderland.lit") }
|
117
117
|
assert_raises(ArgumentError) { Vines::JID.new("alice\ts@wonderland.lit") }
|
118
|
+
assert_raises(ArgumentError) { Vines::JID.new("alice\rs@wonderland.lit") }
|
119
|
+
assert_raises(ArgumentError) { Vines::JID.new("alice\ns@wonderland.lit") }
|
120
|
+
assert_raises(ArgumentError) { Vines::JID.new("alice\vs@wonderland.lit") }
|
121
|
+
assert_raises(ArgumentError) { Vines::JID.new("alice\fs@wonderland.lit") }
|
118
122
|
assert_raises(ArgumentError) { Vines::JID.new(" alice@wonderland.lit") }
|
119
123
|
assert_raises(ArgumentError) { Vines::JID.new("alice@wonderland.lit ") }
|
120
124
|
assert_raises(ArgumentError) { Vines::JID.new("alice s@wonderland.lit") }
|
121
125
|
assert_raises(ArgumentError) { Vines::JID.new("alice@w onderland.lit") }
|
126
|
+
assert_raises(ArgumentError) { Vines::JID.new("alice@w\tonderland.lit") }
|
127
|
+
assert_raises(ArgumentError) { Vines::JID.new("alice@w\ronderland.lit") }
|
128
|
+
assert_raises(ArgumentError) { Vines::JID.new("alice@w\nonderland.lit") }
|
129
|
+
assert_raises(ArgumentError) { Vines::JID.new("alice@w\vonderland.lit") }
|
130
|
+
assert_raises(ArgumentError) { Vines::JID.new("alice@w\fonderland.lit") }
|
122
131
|
assert_raises(ArgumentError) { Vines::JID.new("alice@wonderland.lit/ res") }
|
123
132
|
assert_raises(ArgumentError) { Vines::JID.new("alice@w\u0000onderland.lit") }
|
124
133
|
assert_raises(ArgumentError) { Vines::JID.new("alice@wonderland.lit/\u0000res") }
|