vinesmod 0.4.5
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/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
|