vinesmod 0.4.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +43 -0
- data/Rakefile +57 -0
- data/bin/vines +93 -0
- data/conf/certs/README +39 -0
- data/conf/certs/ca-bundle.crt +3366 -0
- data/conf/config.rb +149 -0
- data/lib/vines.rb +197 -0
- data/lib/vines/cluster.rb +246 -0
- data/lib/vines/cluster/connection.rb +26 -0
- data/lib/vines/cluster/publisher.rb +55 -0
- data/lib/vines/cluster/pubsub.rb +92 -0
- data/lib/vines/cluster/sessions.rb +125 -0
- data/lib/vines/cluster/subscriber.rb +108 -0
- data/lib/vines/command/bcrypt.rb +12 -0
- data/lib/vines/command/cert.rb +50 -0
- data/lib/vines/command/init.rb +68 -0
- data/lib/vines/command/register.rb +27 -0
- data/lib/vines/command/restart.rb +12 -0
- data/lib/vines/command/schema.rb +24 -0
- data/lib/vines/command/start.rb +28 -0
- data/lib/vines/command/stop.rb +18 -0
- data/lib/vines/command/unregister.rb +27 -0
- data/lib/vines/config.rb +213 -0
- data/lib/vines/config/host.rb +119 -0
- data/lib/vines/config/port.rb +132 -0
- data/lib/vines/config/pubsub.rb +108 -0
- data/lib/vines/contact.rb +111 -0
- data/lib/vines/daemon.rb +78 -0
- data/lib/vines/error.rb +150 -0
- data/lib/vines/jid.rb +95 -0
- data/lib/vines/kit.rb +35 -0
- data/lib/vines/log.rb +24 -0
- data/lib/vines/router.rb +179 -0
- data/lib/vines/stanza.rb +175 -0
- data/lib/vines/stanza/iq.rb +48 -0
- data/lib/vines/stanza/iq/auth.rb +18 -0
- data/lib/vines/stanza/iq/disco_info.rb +45 -0
- data/lib/vines/stanza/iq/disco_items.rb +29 -0
- data/lib/vines/stanza/iq/error.rb +16 -0
- data/lib/vines/stanza/iq/ping.rb +16 -0
- data/lib/vines/stanza/iq/private_storage.rb +83 -0
- data/lib/vines/stanza/iq/query.rb +10 -0
- data/lib/vines/stanza/iq/register.rb +42 -0
- data/lib/vines/stanza/iq/result.rb +16 -0
- data/lib/vines/stanza/iq/roster.rb +140 -0
- data/lib/vines/stanza/iq/session.rb +17 -0
- data/lib/vines/stanza/iq/vcard.rb +56 -0
- data/lib/vines/stanza/iq/version.rb +25 -0
- data/lib/vines/stanza/message.rb +43 -0
- data/lib/vines/stanza/presence.rb +156 -0
- data/lib/vines/stanza/presence/error.rb +23 -0
- data/lib/vines/stanza/presence/probe.rb +37 -0
- data/lib/vines/stanza/presence/subscribe.rb +42 -0
- data/lib/vines/stanza/presence/subscribed.rb +51 -0
- data/lib/vines/stanza/presence/unavailable.rb +15 -0
- data/lib/vines/stanza/presence/unsubscribe.rb +38 -0
- data/lib/vines/stanza/presence/unsubscribed.rb +38 -0
- data/lib/vines/stanza/pubsub.rb +22 -0
- data/lib/vines/stanza/pubsub/create.rb +39 -0
- data/lib/vines/stanza/pubsub/delete.rb +41 -0
- data/lib/vines/stanza/pubsub/publish.rb +66 -0
- data/lib/vines/stanza/pubsub/subscribe.rb +44 -0
- data/lib/vines/stanza/pubsub/unsubscribe.rb +30 -0
- data/lib/vines/storage.rb +188 -0
- data/lib/vines/storage/local.rb +165 -0
- data/lib/vines/storage/null.rb +39 -0
- data/lib/vines/storage/sql.rb +260 -0
- data/lib/vines/store.rb +94 -0
- data/lib/vines/stream.rb +247 -0
- data/lib/vines/stream/client.rb +84 -0
- data/lib/vines/stream/client/auth.rb +74 -0
- data/lib/vines/stream/client/auth_restart.rb +29 -0
- data/lib/vines/stream/client/bind.rb +72 -0
- data/lib/vines/stream/client/bind_restart.rb +24 -0
- data/lib/vines/stream/client/closed.rb +13 -0
- data/lib/vines/stream/client/ready.rb +17 -0
- data/lib/vines/stream/client/session.rb +210 -0
- data/lib/vines/stream/client/start.rb +27 -0
- data/lib/vines/stream/client/tls.rb +38 -0
- data/lib/vines/stream/component.rb +58 -0
- data/lib/vines/stream/component/handshake.rb +26 -0
- data/lib/vines/stream/component/ready.rb +23 -0
- data/lib/vines/stream/component/start.rb +19 -0
- data/lib/vines/stream/http.rb +157 -0
- data/lib/vines/stream/http/auth.rb +22 -0
- data/lib/vines/stream/http/bind.rb +32 -0
- data/lib/vines/stream/http/bind_restart.rb +37 -0
- data/lib/vines/stream/http/ready.rb +29 -0
- data/lib/vines/stream/http/request.rb +172 -0
- data/lib/vines/stream/http/session.rb +120 -0
- data/lib/vines/stream/http/sessions.rb +65 -0
- data/lib/vines/stream/http/start.rb +23 -0
- data/lib/vines/stream/parser.rb +78 -0
- data/lib/vines/stream/sasl.rb +92 -0
- data/lib/vines/stream/server.rb +150 -0
- data/lib/vines/stream/server/auth.rb +13 -0
- data/lib/vines/stream/server/auth_restart.rb +13 -0
- data/lib/vines/stream/server/final_restart.rb +21 -0
- data/lib/vines/stream/server/outbound/auth.rb +31 -0
- data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/auth_result.rb +32 -0
- data/lib/vines/stream/server/outbound/final_features.rb +28 -0
- data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/start.rb +20 -0
- data/lib/vines/stream/server/outbound/tls.rb +30 -0
- data/lib/vines/stream/server/outbound/tls_result.rb +34 -0
- data/lib/vines/stream/server/ready.rb +24 -0
- data/lib/vines/stream/server/start.rb +13 -0
- data/lib/vines/stream/server/tls.rb +13 -0
- data/lib/vines/stream/state.rb +60 -0
- data/lib/vines/token_bucket.rb +55 -0
- data/lib/vines/user.rb +123 -0
- data/lib/vines/version.rb +5 -0
- data/lib/vines/xmpp_server.rb +43 -0
- data/vines.gemspec +36 -0
- data/web/404.html +51 -0
- data/web/apple-touch-icon.png +0 -0
- data/web/chat/coffeescripts/chat.coffee +362 -0
- data/web/chat/coffeescripts/init.coffee +15 -0
- data/web/chat/index.html +16 -0
- data/web/chat/javascripts/app.js +1 -0
- data/web/chat/stylesheets/chat.css +144 -0
- data/web/favicon.png +0 -0
- data/web/lib/coffeescripts/button.coffee +25 -0
- data/web/lib/coffeescripts/contact.coffee +32 -0
- data/web/lib/coffeescripts/filter.coffee +49 -0
- data/web/lib/coffeescripts/layout.coffee +30 -0
- data/web/lib/coffeescripts/login.coffee +68 -0
- data/web/lib/coffeescripts/logout.coffee +5 -0
- data/web/lib/coffeescripts/navbar.coffee +84 -0
- data/web/lib/coffeescripts/notification.coffee +14 -0
- data/web/lib/coffeescripts/router.coffee +40 -0
- data/web/lib/coffeescripts/session.coffee +229 -0
- data/web/lib/coffeescripts/transfer.coffee +106 -0
- data/web/lib/images/dark-gray.png +0 -0
- data/web/lib/images/default-user.png +0 -0
- data/web/lib/images/light-gray.png +0 -0
- data/web/lib/images/logo-large.png +0 -0
- data/web/lib/images/logo-small.png +0 -0
- data/web/lib/images/white.png +0 -0
- data/web/lib/javascripts/base.js +12 -0
- data/web/lib/javascripts/icons.js +110 -0
- data/web/lib/javascripts/jquery.cookie.js +91 -0
- data/web/lib/javascripts/jquery.js +4 -0
- data/web/lib/javascripts/raphael.js +6 -0
- data/web/lib/javascripts/strophe.js +1 -0
- data/web/lib/stylesheets/base.css +385 -0
- data/web/lib/stylesheets/login.css +68 -0
- metadata +423 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
module Command
|
5
|
+
class Init
|
6
|
+
def run(opts)
|
7
|
+
raise 'vines init <domain>' unless opts[:args].size == 1
|
8
|
+
domain = opts[:args].first.downcase
|
9
|
+
dir = File.expand_path(domain)
|
10
|
+
raise "Directory already initialized: #{domain}" if File.exists?(dir)
|
11
|
+
Dir.mkdir(dir)
|
12
|
+
|
13
|
+
create_directories(dir)
|
14
|
+
create_users(domain, dir)
|
15
|
+
update_config(domain, dir)
|
16
|
+
Command::Cert.new.create_cert(domain, File.join(dir, 'conf/certs'))
|
17
|
+
|
18
|
+
puts "Initialized server directory: #{domain}"
|
19
|
+
puts "Run 'cd #{domain} && vines start' to begin"
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Limit file system database directory access so the server is the only
|
25
|
+
# process managing the data. The config.rb file contains component and
|
26
|
+
# database passwords, so restrict access to just the server user as well.
|
27
|
+
def create_directories(dir)
|
28
|
+
%w[conf web].each do |sub|
|
29
|
+
FileUtils.cp_r(File.expand_path("../../../../#{sub}", __FILE__), dir)
|
30
|
+
end
|
31
|
+
%w[data log pid].each do |sub|
|
32
|
+
Dir.mkdir(File.join(dir, sub), 0700)
|
33
|
+
end
|
34
|
+
File.chmod(0600, File.join(dir, 'conf/config.rb'))
|
35
|
+
end
|
36
|
+
|
37
|
+
def update_config(domain, dir)
|
38
|
+
config = File.expand_path('conf/config.rb', dir)
|
39
|
+
text = File.read(config, encoding: 'utf-8')
|
40
|
+
File.open(config, 'w:utf-8') do |f|
|
41
|
+
f.write(text.gsub('wonderland.lit', domain))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_users(domain, dir)
|
46
|
+
password = 'secr3t'
|
47
|
+
alice, arthur = %w[alice arthur].map do |jid|
|
48
|
+
User.new(jid: [jid, domain].join('@'),
|
49
|
+
password: BCrypt::Password.create(password).to_s)
|
50
|
+
end
|
51
|
+
|
52
|
+
[[alice, arthur], [arthur, alice]].each do |user, contact|
|
53
|
+
user.roster << Contact.new(
|
54
|
+
jid: contact.jid,
|
55
|
+
name: contact.jid.node.capitalize,
|
56
|
+
subscription: 'both',
|
57
|
+
groups: %w[Buddies])
|
58
|
+
end
|
59
|
+
|
60
|
+
storage = Storage::Local.new { dir(File.join(dir, 'data')) }
|
61
|
+
[alice, arthur].each do |user|
|
62
|
+
storage.save_user(user)
|
63
|
+
puts "Created example user #{user.jid} with password #{password}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
module Command
|
5
|
+
class Register
|
6
|
+
def run(opts)
|
7
|
+
raise 'vines register <login> <password> <domain>' unless opts[:args].size == 3
|
8
|
+
require opts[:config]
|
9
|
+
|
10
|
+
login, password, domain = opts[:args]
|
11
|
+
|
12
|
+
create_user(domain, login, password)
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_user(domain, jid, password)
|
16
|
+
user = User.new(jid: [jid, domain].join('@'),
|
17
|
+
password: BCrypt::Password.create(password).to_s)
|
18
|
+
|
19
|
+
storage = Storage::Local.new { dir(Config.instance.storage(domain).dir) }
|
20
|
+
storage.save_user(user)
|
21
|
+
|
22
|
+
puts "Created user #{user.jid} with password #{password}"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
module Command
|
5
|
+
class Schema
|
6
|
+
def run(opts)
|
7
|
+
raise 'vines schema <domain>' unless opts[:args].size == 1
|
8
|
+
require opts[:config]
|
9
|
+
domain = opts[:args].first
|
10
|
+
unless storage = Config.instance.vhost(domain).storage rescue nil
|
11
|
+
raise "#{domain} virtual host not found in conf/config.rb"
|
12
|
+
end
|
13
|
+
unless storage.respond_to?(:create_schema)
|
14
|
+
raise "SQL storage not configured for #{domain} virtual host"
|
15
|
+
end
|
16
|
+
begin
|
17
|
+
storage.create_schema
|
18
|
+
rescue => e
|
19
|
+
raise "Schema creation failed: #{e.message}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
module Command
|
5
|
+
class Start
|
6
|
+
def run(opts)
|
7
|
+
raise 'vines [--pid FILE] start' unless opts[:args].size == 0
|
8
|
+
require opts[:config]
|
9
|
+
server = XmppServer.new(Config.instance)
|
10
|
+
daemonize(opts) if opts[:daemonize]
|
11
|
+
server.start
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def daemonize(opts)
|
17
|
+
daemon = Daemon.new(:pid => opts[:pid], :stdout => opts[:log],
|
18
|
+
:stderr => opts[:log])
|
19
|
+
if daemon.running?
|
20
|
+
raise "Vines is running as process #{daemon.pid}"
|
21
|
+
else
|
22
|
+
puts "Vines has started"
|
23
|
+
daemon.start
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
module Command
|
5
|
+
class Stop
|
6
|
+
def run(opts)
|
7
|
+
raise 'vines [--pid FILE] stop' unless opts[:args].size == 0
|
8
|
+
daemon = Daemon.new(:pid => opts[:pid])
|
9
|
+
if daemon.running?
|
10
|
+
daemon.stop
|
11
|
+
puts 'Vines has been shutdown'
|
12
|
+
else
|
13
|
+
puts 'Vines is not running'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
module Command
|
5
|
+
class Unregister
|
6
|
+
def run(opts)
|
7
|
+
raise 'vines unregister <login> <password> <domain>' unless opts[:args].size == 3
|
8
|
+
require opts[:config]
|
9
|
+
|
10
|
+
login, password, domain = opts[:args]
|
11
|
+
|
12
|
+
delete_user(domain, login, password)
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete_user(domain, jid, password)
|
16
|
+
user = User.new(jid: [jid, domain].join('@'),
|
17
|
+
password: BCrypt::Password.create(password).to_s)
|
18
|
+
|
19
|
+
storage = Storage::Local.new { dir(Config.instance.storage(domain).dir) }
|
20
|
+
storage.save_user(user)
|
21
|
+
|
22
|
+
puts "Created user #{user.jid} with password #{password}"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/vines/config.rb
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
|
5
|
+
# A Config object is passed to the stream handlers to give them access
|
6
|
+
# to server configuration information like virtual host names, storage
|
7
|
+
# systems, etc. This class provides the DSL methods used in the
|
8
|
+
# conf/config.rb file.
|
9
|
+
class Config
|
10
|
+
LOG_LEVELS = %w[debug info warn error fatal].freeze
|
11
|
+
|
12
|
+
attr_reader :router
|
13
|
+
|
14
|
+
@@instance = nil
|
15
|
+
def self.configure(&block)
|
16
|
+
@@instance = self.new(&block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.instance
|
20
|
+
@@instance
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(&block)
|
24
|
+
@certs = File.expand_path('conf/certs')
|
25
|
+
@vhosts, @ports, @cluster = {}, {}, nil
|
26
|
+
@null = Storage::Null.new
|
27
|
+
@router = Router.new(self)
|
28
|
+
instance_eval(&block)
|
29
|
+
raise "must define at least one virtual host" if @vhosts.empty?
|
30
|
+
|
31
|
+
unless @certs && File.directory?(@certs) && File.readable?(@certs)
|
32
|
+
raise 'Must provide a readable certs directory'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def certs(dir=nil)
|
37
|
+
dir ? @certs = File.expand_path(dir) : @certs
|
38
|
+
end
|
39
|
+
|
40
|
+
def host(*names, &block)
|
41
|
+
names = names.flatten.map {|name| name.downcase }
|
42
|
+
dupes = names.uniq.size != names.size || (@vhosts.keys & names).any?
|
43
|
+
raise "one host definition per domain allowed" if dupes
|
44
|
+
names.each do |name|
|
45
|
+
@vhosts[name] = Host.new(self, name, &block)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
%w[client server http component].each do |name|
|
50
|
+
define_method(name) do |*args, &block|
|
51
|
+
port = Vines::Config.const_get("#{name.capitalize}Port")
|
52
|
+
raise "one #{name} port definition allowed" if @ports[name.to_sym]
|
53
|
+
@ports[name.to_sym] = port.new(self, *args) do
|
54
|
+
instance_eval(&block) if block
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def cluster(&block)
|
60
|
+
return @cluster unless block
|
61
|
+
raise "one cluster definition allowed" if @cluster
|
62
|
+
@cluster = Cluster.new(self, &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
def log(level)
|
66
|
+
const = Logger.const_get(level.to_s.upcase) rescue nil
|
67
|
+
unless LOG_LEVELS.include?(level.to_s) && const
|
68
|
+
raise "log level must be one of: #{LOG_LEVELS.join(', ')}"
|
69
|
+
end
|
70
|
+
Class.new.extend(Vines::Log).log.level = const
|
71
|
+
end
|
72
|
+
|
73
|
+
def ports
|
74
|
+
@ports.values
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return true if the domain is virtual hosted by this server.
|
78
|
+
def vhost?(domain)
|
79
|
+
!!vhost(domain)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Return the Host config object for this domain if it's hosted by this
|
83
|
+
# server.
|
84
|
+
def vhost(domain)
|
85
|
+
@vhosts[domain.to_s]
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns the storage system for the domain or a Storage::Null instance if
|
89
|
+
# the domain is not hosted at this server.
|
90
|
+
def storage(domain)
|
91
|
+
host = vhost(domain)
|
92
|
+
host ? host.storage : @null
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the PubSub system for the domain or nil if pubsub is not enabled
|
96
|
+
# for this domain.
|
97
|
+
def pubsub(domain)
|
98
|
+
host = @vhosts.values.find {|host| host.pubsub?(domain) }
|
99
|
+
host.pubsubs[domain.to_s] if host
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return true if the domain is a pubsub service hosted at a virtual host
|
103
|
+
# at this server.
|
104
|
+
def pubsub?(domain)
|
105
|
+
@vhosts.values.any? {|host| host.pubsub?(domain) }
|
106
|
+
end
|
107
|
+
|
108
|
+
# Return true if all JIDs belong to components hosted by this server.
|
109
|
+
def component?(*jids)
|
110
|
+
!jids.flatten.index do |jid|
|
111
|
+
!component_password(JID.new(jid).domain)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Return the password for the component or nil if it's not hosted here.
|
116
|
+
def component_password(domain)
|
117
|
+
host = @vhosts.values.find {|host| host.component?(domain) }
|
118
|
+
host.password(domain) if host
|
119
|
+
end
|
120
|
+
|
121
|
+
# Return true if all of the JIDs are hosted by this server.
|
122
|
+
def local_jid?(*jids)
|
123
|
+
!jids.flatten.index do |jid|
|
124
|
+
!vhost?(JID.new(jid).domain)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Return true if private XML fragment storage is enabled for this domain.
|
129
|
+
def private_storage?(domain)
|
130
|
+
host = vhost(domain)
|
131
|
+
host.private_storage? if host
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns true if server-to-server connections are allowed with the
|
135
|
+
# given domain.
|
136
|
+
def s2s?(domain)
|
137
|
+
@ports[:server] && @ports[:server].hosts.include?(domain.to_s)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Return true if the server is a member of a cluster, serving the same
|
141
|
+
# domains from different machines.
|
142
|
+
def cluster?
|
143
|
+
!!@cluster
|
144
|
+
end
|
145
|
+
|
146
|
+
# Retrieve the Port subclass with this name:
|
147
|
+
# [:client, :server, :http, :component]
|
148
|
+
def [](name)
|
149
|
+
@ports[name] or raise ArgumentError.new("no port named #{name}")
|
150
|
+
end
|
151
|
+
|
152
|
+
# Return true if the two JIDs are allowed to send messages to each other.
|
153
|
+
# Both domains must have enabled cross_domain_messages in their config files.
|
154
|
+
def allowed?(to, from)
|
155
|
+
to, from = JID.new(to), JID.new(from)
|
156
|
+
return false if to.empty? || from.empty?
|
157
|
+
return true if to.domain == from.domain # same domain always allowed
|
158
|
+
return cross_domain?(to, from) if local_jid?(to, from) # both virtual hosted here
|
159
|
+
return check_subdomains(to, from) if subdomain?(to, from) # component/pubsub to component/pubsub
|
160
|
+
return check_subdomain(to, from) if subdomain?(to) # to component/pubsub
|
161
|
+
return check_subdomain(from, to) if subdomain?(from) # from component/pubsub
|
162
|
+
return cross_domain?(to) if local_jid?(to) # from is remote
|
163
|
+
return cross_domain?(from) if local_jid?(from) # to is remote
|
164
|
+
return false
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
# Return true if all of the JIDs are some kind of subdomain resource hosted
|
170
|
+
# here (either a component or a pubsub domain).
|
171
|
+
def subdomain?(*jids)
|
172
|
+
!jids.flatten.index do |jid|
|
173
|
+
!component?(jid) && !pubsub?(jid)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Return true if the third-level subdomain JIDs (components and pubsubs)
|
178
|
+
# are allowed to communicate with each other. For example, a
|
179
|
+
# tea.wonderland.lit component should be allowed to send messages to
|
180
|
+
# pubsub.wonderland.lit because they share the second-level wonderland.lit
|
181
|
+
# domain.
|
182
|
+
def check_subdomains(to, from)
|
183
|
+
sub1, sub2 = strip_domain(to), strip_domain(from)
|
184
|
+
(sub1 == sub2) || cross_domain?(sub1, sub2)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Return true if the third-level subdomain JID (component or pubsub) is
|
188
|
+
# allowed to communicate with the other JID. For example,
|
189
|
+
# pubsub.wonderland.lit should be allowed to send messages to
|
190
|
+
# alice@wonderland.lit because they share the second-level wonderland.lit
|
191
|
+
# domain.
|
192
|
+
def check_subdomain(subdomain, jid)
|
193
|
+
comp = strip_domain(subdomain)
|
194
|
+
return true if comp.domain == jid.domain
|
195
|
+
local_jid?(jid) ? cross_domain?(comp, jid) : cross_domain?(comp)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Return the third-level JID's domain with the first subdomain stripped off
|
199
|
+
# to create a second-level domain. For example, alice@tea.wonderland.lit
|
200
|
+
# returns wonderland.lit.
|
201
|
+
def strip_domain(jid)
|
202
|
+
domain = jid.domain.split('.').drop(1).join('.')
|
203
|
+
JID.new(domain)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Return true if all JIDs are allowed to exchange cross domain messages.
|
207
|
+
def cross_domain?(*jids)
|
208
|
+
!jids.flatten.index do |jid|
|
209
|
+
!vhost(jid.domain).cross_domain_messages?
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Config
|
5
|
+
|
6
|
+
# Provides the DSL methods for the virtual host definitions in the
|
7
|
+
# conf/config.rb file. Host instances can be accessed at runtime through
|
8
|
+
# the +Config#vhosts+ method.
|
9
|
+
class Host
|
10
|
+
attr_reader :pubsubs
|
11
|
+
|
12
|
+
def initialize(config, name, &block)
|
13
|
+
@config, @name = config, name.downcase
|
14
|
+
@storage = nil
|
15
|
+
@cross_domain_messages = false
|
16
|
+
@private_storage = false
|
17
|
+
@components, @pubsubs = {}, {}
|
18
|
+
validate_domain(@name)
|
19
|
+
instance_eval(&block)
|
20
|
+
raise "storage required for #{@name}" unless @storage
|
21
|
+
end
|
22
|
+
|
23
|
+
def storage(name=nil, &block)
|
24
|
+
if name
|
25
|
+
raise "one storage mechanism per host allowed" if @storage
|
26
|
+
@storage = Storage.from_name(name, &block)
|
27
|
+
else
|
28
|
+
@storage
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def cross_domain_messages(enabled)
|
33
|
+
@cross_domain_messages = !!enabled
|
34
|
+
end
|
35
|
+
|
36
|
+
def cross_domain_messages?
|
37
|
+
@cross_domain_messages
|
38
|
+
end
|
39
|
+
|
40
|
+
def components(options=nil)
|
41
|
+
return @components unless options
|
42
|
+
|
43
|
+
names = options.keys.map {|domain| "#{domain}.#{@name}".downcase }
|
44
|
+
raise "duplicate component domains not allowed" if dupes?(names, @components.keys)
|
45
|
+
raise "pubsub domains overlap component domains" if dupes?(names, @pubsubs.keys)
|
46
|
+
|
47
|
+
options.each do |domain, password|
|
48
|
+
raise 'component domain required' if (domain || '').to_s.strip.empty?
|
49
|
+
raise 'component password required' if (password || '').strip.empty?
|
50
|
+
name = "#{domain}.#{@name}".downcase
|
51
|
+
raise "components must be one level below their host: #{name}" if domain.to_s.include?('.')
|
52
|
+
validate_domain(name)
|
53
|
+
@components[name] = password
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def component?(domain)
|
58
|
+
!!@components[domain.to_s]
|
59
|
+
end
|
60
|
+
|
61
|
+
def password(domain)
|
62
|
+
@components[domain.to_s]
|
63
|
+
end
|
64
|
+
|
65
|
+
def pubsub(*domains)
|
66
|
+
domains.flatten!
|
67
|
+
raise 'define at least one pubsub domain' if domains.empty?
|
68
|
+
names = domains.map {|domain| "#{domain}.#{@name}".downcase }
|
69
|
+
raise "duplicate pubsub domains not allowed" if dupes?(names, @pubsubs.keys)
|
70
|
+
raise "pubsub domains overlap component domains" if dupes?(names, @components.keys)
|
71
|
+
domains.each do |domain|
|
72
|
+
raise 'pubsub domain required' if (domain || '').to_s.strip.empty?
|
73
|
+
name = "#{domain}.#{@name}".downcase
|
74
|
+
raise "pubsub domains must be one level below their host: #{name}" if domain.to_s.include?('.')
|
75
|
+
validate_domain(name)
|
76
|
+
@pubsubs[name] = PubSub.new(@config, name)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def pubsub?(domain)
|
81
|
+
@pubsubs.key?(domain.to_s)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Unsubscribe this JID from all pubsub topics hosted at this virtual host.
|
85
|
+
# This should be called when the user's session ends via logout or
|
86
|
+
# disconnect.
|
87
|
+
def unsubscribe_pubsub(jid)
|
88
|
+
@pubsubs.values.each do |pubsub|
|
89
|
+
pubsub.unsubscribe_all(jid)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def disco_items
|
94
|
+
[@components.keys, @pubsubs.keys].flatten.sort
|
95
|
+
end
|
96
|
+
|
97
|
+
def private_storage(enabled)
|
98
|
+
@private_storage = !!enabled
|
99
|
+
end
|
100
|
+
|
101
|
+
def private_storage?
|
102
|
+
@private_storage
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# Return true if the arrays contain any duplicate items.
|
108
|
+
def dupes?(a, b)
|
109
|
+
a.uniq.size != a.size || b.uniq.size != b.size || (a & b).any?
|
110
|
+
end
|
111
|
+
|
112
|
+
# Prevent domains in config files that won't form valid JIDs.
|
113
|
+
def validate_domain(name)
|
114
|
+
jid = JID.new(name)
|
115
|
+
raise "incorrect domain: #{name}" if jid.node || jid.resource
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|