vines 0.4.7 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +1 -1
- data/bin/vines +1 -101
- data/lib/vines.rb +2 -0
- data/lib/vines/cli.rb +132 -0
- data/lib/vines/stream/sasl.rb +51 -15
- data/lib/vines/version.rb +1 -1
- data/test/stream/sasl_test.rb +12 -0
- metadata +8 -28
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8a283e328bc02906cb090b08bcdc5c5bbc954a93
|
4
|
+
data.tar.gz: 37528ba4506f7a6bf6f57a569696eb80e7eed6d1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 16915f6f177f20dd6311d6d57df91bf3981382f38d3ab50d94979d8813db203a20d027e6bde673d5086a637b6db4f140c324d2519866dcd2d8476d79e1d86393
|
7
|
+
data.tar.gz: 7cbf9026d98a8d1452af15ffecf2d7544222086fbb0a11ee9ba2e965c0644feefb163dd2e6ea77e0bab881e9b3c724de46eeb3251dbe90d3c3d0b8ed1177e3d6
|
data/LICENSE
CHANGED
data/bin/vines
CHANGED
@@ -1,104 +1,4 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
# encoding: UTF-8
|
3
2
|
|
4
|
-
require 'optparse'
|
5
3
|
require 'vines'
|
6
|
-
|
7
|
-
COMMANDS = %w[start stop restart init bcrypt cert ldap schema]
|
8
|
-
|
9
|
-
def register_storage
|
10
|
-
%w[couchdb mongodb redis sql].each do |backend|
|
11
|
-
begin
|
12
|
-
require 'vines/storage/%s' % backend
|
13
|
-
rescue LoadError
|
14
|
-
# do nothing
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def parse(args)
|
20
|
-
options = {}
|
21
|
-
parser = OptionParser.new do |opts|
|
22
|
-
opts.banner = "Usage: vines [options] #{COMMANDS.join('|')}"
|
23
|
-
|
24
|
-
opts.separator ""
|
25
|
-
opts.separator "Daemon options:"
|
26
|
-
|
27
|
-
opts.on('-d', '--daemonize', 'Run daemonized in the background') do |daemonize|
|
28
|
-
options[:daemonize] = daemonize
|
29
|
-
end
|
30
|
-
|
31
|
-
options[:log] = 'log/vines.log'
|
32
|
-
opts.on('-l', '--log FILE', 'File to redirect output (default: log/vines.log)') do |log|
|
33
|
-
options[:log] = log
|
34
|
-
end
|
35
|
-
|
36
|
-
options[:pid] = 'pid/vines.pid'
|
37
|
-
opts.on('-P', '--pid FILE', 'File to store PID (default: pid/vines.pid)') do |pid|
|
38
|
-
options[:pid] = pid
|
39
|
-
end
|
40
|
-
|
41
|
-
opts.separator ""
|
42
|
-
opts.separator "Common options:"
|
43
|
-
|
44
|
-
opts.on('-h', '--help', 'Show this message') do |help|
|
45
|
-
options[:help] = help
|
46
|
-
end
|
47
|
-
|
48
|
-
opts.on('-v', '--version', 'Show version') do |version|
|
49
|
-
options[:version] = version
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
begin
|
54
|
-
parser.parse!(args)
|
55
|
-
rescue
|
56
|
-
puts parser
|
57
|
-
exit(1)
|
58
|
-
end
|
59
|
-
|
60
|
-
if options[:version]
|
61
|
-
puts Vines::VERSION
|
62
|
-
exit
|
63
|
-
end
|
64
|
-
|
65
|
-
if options[:help]
|
66
|
-
puts parser
|
67
|
-
exit
|
68
|
-
end
|
69
|
-
|
70
|
-
command = args.shift
|
71
|
-
unless COMMANDS.include?(command)
|
72
|
-
puts parser
|
73
|
-
exit(1)
|
74
|
-
end
|
75
|
-
|
76
|
-
options.tap do |opts|
|
77
|
-
opts[:args] = args
|
78
|
-
opts[:command] = command
|
79
|
-
opts[:config] = File.expand_path('conf/config.rb')
|
80
|
-
opts[:pid] = File.expand_path(opts[:pid])
|
81
|
-
opts[:log] = File.expand_path(opts[:log])
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def check_config(opts)
|
86
|
-
return if %w[bcrypt init].include?(opts[:command])
|
87
|
-
unless File.exists?(opts[:config])
|
88
|
-
puts "No config file found at #{opts[:config]}"
|
89
|
-
exit(1)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
register_storage
|
94
|
-
opts = parse(ARGV)
|
95
|
-
check_config(opts)
|
96
|
-
command = Vines::Command.const_get(opts[:command].capitalize).new
|
97
|
-
begin
|
98
|
-
command.run(opts)
|
99
|
-
rescue SystemExit
|
100
|
-
# do nothing
|
101
|
-
rescue Exception => e
|
102
|
-
puts e.message
|
103
|
-
exit(1)
|
104
|
-
end
|
4
|
+
Vines::CLI.start
|
data/lib/vines.rb
CHANGED
data/lib/vines/cli.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
module Vines
|
2
|
+
# The command line application that's invoked by the `vines` binary included
|
3
|
+
# in the gem. Parses the command line arguments to create a new server
|
4
|
+
# directory, and starts and stops the server.
|
5
|
+
class CLI
|
6
|
+
COMMANDS = %w[start stop restart init bcrypt cert ldap schema]
|
7
|
+
|
8
|
+
def self.start
|
9
|
+
self.new.start
|
10
|
+
end
|
11
|
+
|
12
|
+
# Run the command line application to parse arguments and run sub-commands.
|
13
|
+
# Exits the process with a non-zero return code to indicate failure.
|
14
|
+
#
|
15
|
+
# Returns nothing.
|
16
|
+
def start
|
17
|
+
register_storage
|
18
|
+
opts = parse(ARGV)
|
19
|
+
check_config(opts)
|
20
|
+
command = Command.const_get(opts[:command].capitalize).new
|
21
|
+
begin
|
22
|
+
command.run(opts)
|
23
|
+
rescue SystemExit
|
24
|
+
# do nothing
|
25
|
+
rescue Exception => e
|
26
|
+
puts e.message
|
27
|
+
exit(1)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Try to load various storage backends provided by vines-* gems and register
|
34
|
+
# them with the storage system for the config file to use.
|
35
|
+
#
|
36
|
+
# Returns nothing.
|
37
|
+
def register_storage
|
38
|
+
%w[couchdb mongodb redis sql].each do |backend|
|
39
|
+
begin
|
40
|
+
require 'vines/storage/%s' % backend
|
41
|
+
rescue LoadError
|
42
|
+
# do nothing
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Parse the command line arguments and run the matching sub-command
|
48
|
+
# (e.g. init, start, stop, etc).
|
49
|
+
#
|
50
|
+
# args - The ARGV array provided by the command line.
|
51
|
+
#
|
52
|
+
# Returns nothing.
|
53
|
+
def parse(args)
|
54
|
+
options = {}
|
55
|
+
parser = OptionParser.new do |opts|
|
56
|
+
opts.banner = "Usage: vines [options] #{COMMANDS.join('|')}"
|
57
|
+
|
58
|
+
opts.separator ""
|
59
|
+
opts.separator "Daemon options:"
|
60
|
+
|
61
|
+
opts.on('-d', '--daemonize', 'Run daemonized in the background') do |daemonize|
|
62
|
+
options[:daemonize] = daemonize
|
63
|
+
end
|
64
|
+
|
65
|
+
options[:log] = 'log/vines.log'
|
66
|
+
opts.on('-l', '--log FILE', 'File to redirect output (default: log/vines.log)') do |log|
|
67
|
+
options[:log] = log
|
68
|
+
end
|
69
|
+
|
70
|
+
options[:pid] = 'pid/vines.pid'
|
71
|
+
opts.on('-P', '--pid FILE', 'File to store PID (default: pid/vines.pid)') do |pid|
|
72
|
+
options[:pid] = pid
|
73
|
+
end
|
74
|
+
|
75
|
+
opts.separator ""
|
76
|
+
opts.separator "Common options:"
|
77
|
+
|
78
|
+
opts.on('-h', '--help', 'Show this message') do |help|
|
79
|
+
options[:help] = help
|
80
|
+
end
|
81
|
+
|
82
|
+
opts.on('-v', '--version', 'Show version') do |version|
|
83
|
+
options[:version] = version
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
begin
|
88
|
+
parser.parse!(args)
|
89
|
+
rescue
|
90
|
+
puts parser
|
91
|
+
exit(1)
|
92
|
+
end
|
93
|
+
|
94
|
+
if options[:version]
|
95
|
+
puts Vines::VERSION
|
96
|
+
exit
|
97
|
+
end
|
98
|
+
|
99
|
+
if options[:help]
|
100
|
+
puts parser
|
101
|
+
exit
|
102
|
+
end
|
103
|
+
|
104
|
+
command = args.shift
|
105
|
+
unless COMMANDS.include?(command)
|
106
|
+
puts parser
|
107
|
+
exit(1)
|
108
|
+
end
|
109
|
+
|
110
|
+
options.tap do |opts|
|
111
|
+
opts[:args] = args
|
112
|
+
opts[:command] = command
|
113
|
+
opts[:config] = File.expand_path('conf/config.rb')
|
114
|
+
opts[:pid] = File.expand_path(opts[:pid])
|
115
|
+
opts[:log] = File.expand_path(opts[:log])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Many commands must be run in the context of a vines server directory
|
120
|
+
# created with `vines init`. If the command can't find the server's config
|
121
|
+
# file, print an error message and exit.
|
122
|
+
#
|
123
|
+
# Returns nothing.
|
124
|
+
def check_config(opts)
|
125
|
+
return if %w[bcrypt init].include?(opts[:command])
|
126
|
+
unless File.exists?(opts[:config])
|
127
|
+
puts "No config file found at #{opts[:config]}"
|
128
|
+
exit(1)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/vines/stream/sasl.rb
CHANGED
@@ -12,11 +12,18 @@ module Vines
|
|
12
12
|
@stream = stream
|
13
13
|
end
|
14
14
|
|
15
|
-
# Authenticate
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
15
|
+
# Authenticate server-to-server streams, comparing their domain to their
|
16
|
+
# SSL certificate.
|
17
|
+
#
|
18
|
+
# http://xmpp.org/extensions/xep-0178.html#s2s
|
19
|
+
#
|
20
|
+
# encoded - The Base64 encoded remote domain name String sent by the
|
21
|
+
# server stream.
|
22
|
+
#
|
23
|
+
# Returns true if the Base64 encoded domain matches the TLS certificate
|
24
|
+
# presented earlier in stream negotiation.
|
25
|
+
#
|
26
|
+
# Raises a SaslError if authentication failed.
|
20
27
|
def external_auth(encoded)
|
21
28
|
unless encoded == EMPTY
|
22
29
|
authzid = decode64(encoded)
|
@@ -27,8 +34,12 @@ module Vines
|
|
27
34
|
matches_from or raise SaslErrors::NotAuthorized
|
28
35
|
end
|
29
36
|
|
30
|
-
# Authenticate
|
31
|
-
#
|
37
|
+
# Authenticate client-to-server streams using a username and password.
|
38
|
+
#
|
39
|
+
# encoded - The Base64 encoded jid and password String sent by the
|
40
|
+
# client stream.
|
41
|
+
#
|
42
|
+
# Returns the authenticated User or raises SaslError if authentication failed.
|
32
43
|
def plain_auth(encoded)
|
33
44
|
jid, password = decode_credentials(encoded)
|
34
45
|
user = authenticate(jid, password)
|
@@ -39,7 +50,13 @@ module Vines
|
|
39
50
|
|
40
51
|
# Storage backends should not raise errors, but if an unexpected error
|
41
52
|
# occurs during authentication, convert it to a temporary-auth-failure.
|
42
|
-
#
|
53
|
+
#
|
54
|
+
# jid - The user's jid String.
|
55
|
+
# password - The String password.
|
56
|
+
#
|
57
|
+
# Returns the authenticated User or nil if authentication failed.
|
58
|
+
#
|
59
|
+
# Raises TemoraryAuthFailure if the storage system failed.
|
43
60
|
def authenticate(jid, password)
|
44
61
|
log.info("Authenticating user: %s" % jid)
|
45
62
|
@stream.storage.authenticate(jid, password).tap do |user|
|
@@ -50,10 +67,15 @@ module Vines
|
|
50
67
|
raise SaslErrors::TemporaryAuthFailure
|
51
68
|
end
|
52
69
|
|
53
|
-
# Return the
|
70
|
+
# Return the JID and password decoded from the Base64 encoded SASL PLAIN
|
54
71
|
# credentials formatted as authzid\0authcid\0password.
|
55
|
-
#
|
56
|
-
#
|
72
|
+
#
|
73
|
+
# http://tools.ietf.org/html/rfc6120#section-6.3.8
|
74
|
+
# http://tools.ietf.org/html/rfc4616
|
75
|
+
#
|
76
|
+
# encoded - The Base64 encoded String from which to extract jid and password.
|
77
|
+
#
|
78
|
+
# Returns an Array of jid String and password String.
|
57
79
|
def decode_credentials(encoded)
|
58
80
|
authzid, node, password = decode64(encoded).split("\x00")
|
59
81
|
raise SaslErrors::NotAuthorized if node.nil? || node.empty? || password.nil? || password.empty?
|
@@ -71,7 +93,13 @@ module Vines
|
|
71
93
|
# widely supported. However, Strophe and Blather send a bare JID, in
|
72
94
|
# compliance with RFC 6120, but Smack sends just the user name as the
|
73
95
|
# authzid. So, take care to handle non-compliant clients here.
|
74
|
-
#
|
96
|
+
#
|
97
|
+
# http://tools.ietf.org/html/rfc6120#section-6.3.8
|
98
|
+
#
|
99
|
+
# authzid - The authzid String (may be nil).
|
100
|
+
# jid - The username String.
|
101
|
+
#
|
102
|
+
# Returns nothing.
|
75
103
|
def validate_authzid!(authzid, jid)
|
76
104
|
return if authzid.nil? || authzid.empty?
|
77
105
|
authzid.downcase!
|
@@ -80,10 +108,18 @@ module Vines
|
|
80
108
|
raise SaslErrors::InvalidAuthzid unless compliant || smack
|
81
109
|
end
|
82
110
|
|
83
|
-
# Decode the
|
84
|
-
#
|
111
|
+
# Decode the Base64 encoded string, raising an error for invalid data.
|
112
|
+
#
|
113
|
+
# http://tools.ietf.org/html/rfc6120#section-13.9.1
|
114
|
+
#
|
115
|
+
# encoded - The Base64 encoded String.
|
116
|
+
#
|
117
|
+
# Returns a UTF-8 String.
|
85
118
|
def decode64(encoded)
|
86
|
-
Base64.strict_decode64(encoded)
|
119
|
+
Base64.strict_decode64(encoded).tap do |decoded|
|
120
|
+
decoded.force_encoding(Encoding::UTF_8)
|
121
|
+
raise SaslErrors::IncorrectEncoding unless decoded.valid_encoding?
|
122
|
+
end
|
87
123
|
rescue
|
88
124
|
raise SaslErrors::IncorrectEncoding
|
89
125
|
end
|
data/lib/vines/version.rb
CHANGED
data/test/stream/sasl_test.rb
CHANGED
@@ -97,6 +97,18 @@ describe Vines::Stream::SASL do
|
|
97
97
|
storage.verify
|
98
98
|
end
|
99
99
|
|
100
|
+
it 'passes with valid password and unicode jid' do
|
101
|
+
user = Vines::User.new(jid: 'piñata@verona.lit')
|
102
|
+
storage.expect :authenticate, user, [user.jid, 'secr3t']
|
103
|
+
stream.expect :storage, storage
|
104
|
+
|
105
|
+
encoded = Base64.strict_encode64("\x00piñata\x00secr3t")
|
106
|
+
subject.plain_auth(encoded).must_equal user
|
107
|
+
|
108
|
+
stream.verify
|
109
|
+
storage.verify
|
110
|
+
end
|
111
|
+
|
100
112
|
it 'passes with valid password and authzid provided by strophe and blather' do
|
101
113
|
storage.expect :authenticate, romeo, [romeo.jid, 'secr3t']
|
102
114
|
stream.expect :storage, storage
|
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vines
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
5
|
-
prerelease:
|
4
|
+
version: 0.4.8
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- David Graham
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-10-30 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: bcrypt-ruby
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ~>
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -30,7 +27,6 @@ dependencies:
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: em-hiredis
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
31
|
- - ~>
|
36
32
|
- !ruby/object:Gem::Version
|
@@ -38,7 +34,6 @@ dependencies:
|
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
38
|
- - ~>
|
44
39
|
- !ruby/object:Gem::Version
|
@@ -46,7 +41,6 @@ dependencies:
|
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: eventmachine
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
45
|
- - ~>
|
52
46
|
- !ruby/object:Gem::Version
|
@@ -54,7 +48,6 @@ dependencies:
|
|
54
48
|
type: :runtime
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
52
|
- - ~>
|
60
53
|
- !ruby/object:Gem::Version
|
@@ -62,7 +55,6 @@ dependencies:
|
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: http_parser.rb
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
59
|
- - ~>
|
68
60
|
- !ruby/object:Gem::Version
|
@@ -70,7 +62,6 @@ dependencies:
|
|
70
62
|
type: :runtime
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
66
|
- - ~>
|
76
67
|
- !ruby/object:Gem::Version
|
@@ -78,7 +69,6 @@ dependencies:
|
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: net-ldap
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
73
|
- - ~>
|
84
74
|
- !ruby/object:Gem::Version
|
@@ -86,7 +76,6 @@ dependencies:
|
|
86
76
|
type: :runtime
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
80
|
- - ~>
|
92
81
|
- !ruby/object:Gem::Version
|
@@ -94,7 +83,6 @@ dependencies:
|
|
94
83
|
- !ruby/object:Gem::Dependency
|
95
84
|
name: nokogiri
|
96
85
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
86
|
requirements:
|
99
87
|
- - ~>
|
100
88
|
- !ruby/object:Gem::Version
|
@@ -102,7 +90,6 @@ dependencies:
|
|
102
90
|
type: :runtime
|
103
91
|
prerelease: false
|
104
92
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
93
|
requirements:
|
107
94
|
- - ~>
|
108
95
|
- !ruby/object:Gem::Version
|
@@ -110,7 +97,6 @@ dependencies:
|
|
110
97
|
- !ruby/object:Gem::Dependency
|
111
98
|
name: minitest
|
112
99
|
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
100
|
requirements:
|
115
101
|
- - ~>
|
116
102
|
- !ruby/object:Gem::Version
|
@@ -118,7 +104,6 @@ dependencies:
|
|
118
104
|
type: :development
|
119
105
|
prerelease: false
|
120
106
|
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
107
|
requirements:
|
123
108
|
- - ~>
|
124
109
|
- !ruby/object:Gem::Version
|
@@ -126,7 +111,6 @@ dependencies:
|
|
126
111
|
- !ruby/object:Gem::Dependency
|
127
112
|
name: rake
|
128
113
|
requirement: !ruby/object:Gem::Requirement
|
129
|
-
none: false
|
130
114
|
requirements:
|
131
115
|
- - ~>
|
132
116
|
- !ruby/object:Gem::Version
|
@@ -134,7 +118,6 @@ dependencies:
|
|
134
118
|
type: :development
|
135
119
|
prerelease: false
|
136
120
|
version_requirements: !ruby/object:Gem::Requirement
|
137
|
-
none: false
|
138
121
|
requirements:
|
139
122
|
- - ~>
|
140
123
|
- !ruby/object:Gem::Version
|
@@ -154,6 +137,7 @@ files:
|
|
154
137
|
- README.md
|
155
138
|
- vines.gemspec
|
156
139
|
- bin/vines
|
140
|
+
- lib/vines/cli.rb
|
157
141
|
- lib/vines/cluster/connection.rb
|
158
142
|
- lib/vines/cluster/publisher.rb
|
159
143
|
- lib/vines/cluster/pubsub.rb
|
@@ -324,30 +308,26 @@ files:
|
|
324
308
|
homepage: http://www.getvines.org
|
325
309
|
licenses:
|
326
310
|
- MIT
|
311
|
+
metadata: {}
|
327
312
|
post_install_message:
|
328
313
|
rdoc_options: []
|
329
314
|
require_paths:
|
330
315
|
- lib
|
331
316
|
required_ruby_version: !ruby/object:Gem::Requirement
|
332
|
-
none: false
|
333
317
|
requirements:
|
334
|
-
- -
|
318
|
+
- - '>='
|
335
319
|
- !ruby/object:Gem::Version
|
336
320
|
version: 1.9.3
|
337
321
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
338
|
-
none: false
|
339
322
|
requirements:
|
340
|
-
- -
|
323
|
+
- - '>='
|
341
324
|
- !ruby/object:Gem::Version
|
342
325
|
version: '0'
|
343
|
-
segments:
|
344
|
-
- 0
|
345
|
-
hash: -1025629835892307549
|
346
326
|
requirements: []
|
347
327
|
rubyforge_project:
|
348
|
-
rubygems_version:
|
328
|
+
rubygems_version: 2.0.3
|
349
329
|
signing_key:
|
350
|
-
specification_version:
|
330
|
+
specification_version: 4
|
351
331
|
summary: Vines is an XMPP chat server that's easy to install and run.
|
352
332
|
test_files:
|
353
333
|
- test/cluster/publisher_test.rb
|