vines 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +5 -9
- data/Rakefile +11 -9
- data/conf/config.rb +30 -4
- 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/cluster.rb +246 -0
- data/lib/vines/command/init.rb +21 -24
- data/lib/vines/config/host.rb +48 -8
- data/lib/vines/config/port.rb +5 -0
- data/lib/vines/config/pubsub.rb +108 -0
- data/lib/vines/config.rb +74 -20
- data/lib/vines/jid.rb +14 -0
- data/lib/vines/router.rb +69 -55
- data/lib/vines/stanza/iq/disco_info.rb +22 -9
- data/lib/vines/stanza/iq/disco_items.rb +6 -3
- data/lib/vines/stanza/iq/ping.rb +1 -1
- data/lib/vines/stanza/iq/private_storage.rb +4 -8
- data/lib/vines/stanza/iq/roster.rb +6 -14
- data/lib/vines/stanza/iq/session.rb +2 -7
- data/lib/vines/stanza/iq/vcard.rb +4 -6
- data/lib/vines/stanza/iq/version.rb +1 -1
- data/lib/vines/stanza/iq.rb +8 -10
- data/lib/vines/stanza/presence/subscribe.rb +3 -11
- data/lib/vines/stanza/presence/subscribed.rb +16 -29
- data/lib/vines/stanza/presence/unsubscribe.rb +3 -15
- data/lib/vines/stanza/presence/unsubscribed.rb +3 -16
- data/lib/vines/stanza/presence.rb +30 -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/stanza/pubsub.rb +22 -0
- data/lib/vines/stanza.rb +72 -22
- data/lib/vines/storage/couchdb.rb +46 -65
- data/lib/vines/storage/local.rb +20 -14
- data/lib/vines/storage/mongodb.rb +132 -0
- data/lib/vines/storage/null.rb +39 -0
- data/lib/vines/storage/redis.rb +61 -68
- data/lib/vines/storage/sql.rb +73 -69
- data/lib/vines/storage.rb +1 -1
- data/lib/vines/stream/client/bind.rb +2 -2
- data/lib/vines/stream/client/session.rb +71 -16
- data/lib/vines/stream/component/handshake.rb +1 -0
- data/lib/vines/stream/component/ready.rb +2 -2
- data/lib/vines/stream/http/session.rb +2 -0
- data/lib/vines/stream/http.rb +0 -6
- data/lib/vines/stream/server/final_restart.rb +1 -0
- data/lib/vines/stream/server/outbound/final_features.rb +1 -0
- data/lib/vines/stream/server/ready.rb +6 -2
- data/lib/vines/stream/server.rb +4 -3
- data/lib/vines/stream.rb +10 -6
- data/lib/vines/version.rb +1 -1
- data/lib/vines.rb +48 -22
- data/test/cluster/publisher_test.rb +45 -0
- data/test/cluster/sessions_test.rb +54 -0
- data/test/cluster/subscriber_test.rb +94 -0
- data/test/config/host_test.rb +100 -21
- data/test/config/pubsub_test.rb +181 -0
- data/test/config_test.rb +225 -43
- data/test/jid_test.rb +7 -0
- data/test/router_test.rb +181 -9
- data/test/stanza/iq/disco_info_test.rb +8 -6
- data/test/stanza/iq/disco_items_test.rb +3 -3
- data/test/stanza/iq/private_storage_test.rb +8 -19
- data/test/stanza/iq/roster_test.rb +1 -1
- data/test/stanza/iq/session_test.rb +3 -6
- data/test/stanza/iq/vcard_test.rb +6 -2
- data/test/stanza/iq/version_test.rb +3 -2
- data/test/stanza/iq_test.rb +5 -5
- data/test/stanza/message_test.rb +3 -2
- data/test/stanza/presence/probe_test.rb +2 -1
- data/test/stanza/pubsub/create_test.rb +138 -0
- data/test/stanza/pubsub/delete_test.rb +142 -0
- data/test/stanza/pubsub/publish_test.rb +373 -0
- data/test/stanza/pubsub/subscribe_test.rb +186 -0
- data/test/stanza/pubsub/unsubscribe_test.rb +179 -0
- data/test/stanza_test.rb +2 -1
- data/test/storage/local_test.rb +26 -25
- data/test/storage/mock_mongo.rb +40 -0
- data/test/storage/mock_redis.rb +98 -0
- data/test/storage/mongodb_test.rb +81 -0
- data/test/storage/null_test.rb +30 -0
- data/test/storage/redis_test.rb +3 -36
- data/test/stream/component/handshake_test.rb +4 -0
- data/test/stream/component/ready_test.rb +2 -1
- data/test/stream/server/ready_test.rb +7 -1
- data/web/404.html +5 -3
- data/web/chat/coffeescripts/chat.coffee +9 -5
- data/web/chat/javascripts/app.js +1 -1
- data/web/chat/javascripts/chat.js +14 -8
- data/web/chat/stylesheets/chat.css +4 -1
- data/web/lib/coffeescripts/button.coffee +9 -5
- data/web/lib/coffeescripts/filter.coffee +1 -1
- data/web/lib/coffeescripts/login.coffee +14 -1
- data/web/lib/coffeescripts/session.coffee +8 -11
- data/web/lib/images/dark-gray.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 +9 -8
- data/web/lib/javascripts/button.js +20 -12
- data/web/lib/javascripts/filter.js +1 -1
- data/web/lib/javascripts/icons.js +7 -1
- data/web/lib/javascripts/jquery.js +4 -4
- data/web/lib/javascripts/login.js +16 -2
- data/web/lib/javascripts/raphael.js +5 -7
- data/web/lib/javascripts/session.js +10 -14
- data/web/lib/stylesheets/base.css +7 -11
- data/web/lib/stylesheets/login.css +31 -27
- metadata +100 -34
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 :vhosts
|
12
|
+
attr_reader :router, :vhosts
|
13
13
|
|
14
14
|
@@instance = nil
|
15
15
|
def self.configure(&block)
|
@@ -21,7 +21,9 @@ module Vines
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def initialize(&block)
|
24
|
-
@vhosts, @ports = {}, {}
|
24
|
+
@vhosts, @ports, @cluster = {}, {}, nil
|
25
|
+
@null = Storage::Null.new
|
26
|
+
@router = Router.new(self)
|
25
27
|
instance_eval(&block)
|
26
28
|
raise "must define at least one virtual host" if @vhosts.empty?
|
27
29
|
end
|
@@ -31,7 +33,7 @@ module Vines
|
|
31
33
|
dupes = names.uniq.size != names.size || (@vhosts.keys & names).any?
|
32
34
|
raise "one host definition per domain allowed" if dupes
|
33
35
|
names.each do |name|
|
34
|
-
@vhosts[name] = Host.new(name, &block)
|
36
|
+
@vhosts[name] = Host.new(self, name, &block)
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
@@ -45,6 +47,12 @@ module Vines
|
|
45
47
|
end
|
46
48
|
end
|
47
49
|
|
50
|
+
def cluster(&block)
|
51
|
+
return @cluster unless block
|
52
|
+
raise "one cluster definition allowed" if @cluster
|
53
|
+
@cluster = Cluster.new(self, &block)
|
54
|
+
end
|
55
|
+
|
48
56
|
def log(level)
|
49
57
|
const = Logger.const_get(level.to_s.upcase) rescue nil
|
50
58
|
unless LOG_LEVELS.include?(level.to_s) && const
|
@@ -59,10 +67,30 @@ module Vines
|
|
59
67
|
|
60
68
|
# Return true if the domain is virtual hosted by this server.
|
61
69
|
def vhost?(domain)
|
62
|
-
@vhosts.key?(domain)
|
70
|
+
@vhosts.key?(domain.to_s)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the storage system for the domain or a Storage::Null instance if
|
74
|
+
# the domain is not hosted at this server.
|
75
|
+
def storage(domain)
|
76
|
+
host = @vhosts[domain.to_s]
|
77
|
+
host ? host.storage : @null
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the PubSub system for the domain or nil if pubsub is not enabled
|
81
|
+
# for this domain.
|
82
|
+
def pubsub(domain)
|
83
|
+
host = @vhosts.values.find {|host| host.pubsub?(domain) }
|
84
|
+
host.pubsubs[domain.to_s] if host
|
85
|
+
end
|
86
|
+
|
87
|
+
# Return true if the domain is a pubsub service hosted at a virtual host
|
88
|
+
# at this server.
|
89
|
+
def pubsub?(domain)
|
90
|
+
@vhosts.values.any? {|host| host.pubsub?(domain) }
|
63
91
|
end
|
64
92
|
|
65
|
-
# Return true if all
|
93
|
+
# Return true if all JIDs belong to components hosted by this server.
|
66
94
|
def component?(*jids)
|
67
95
|
!jids.flatten.index do |jid|
|
68
96
|
!component_password(JID.new(jid).domain)
|
@@ -75,7 +103,7 @@ module Vines
|
|
75
103
|
host.password(domain) if host
|
76
104
|
end
|
77
105
|
|
78
|
-
# Return true if all of the
|
106
|
+
# Return true if all of the JIDs are hosted by this server.
|
79
107
|
def local_jid?(*jids)
|
80
108
|
!jids.flatten.index do |jid|
|
81
109
|
!vhost?(JID.new(jid).domain)
|
@@ -84,13 +112,20 @@ module Vines
|
|
84
112
|
|
85
113
|
# Return true if private XML fragment storage is enabled for this domain.
|
86
114
|
def private_storage?(domain)
|
87
|
-
@vhosts[domain]
|
115
|
+
host = @vhosts[domain.to_s]
|
116
|
+
host.private_storage? if host
|
88
117
|
end
|
89
118
|
|
90
119
|
# Returns true if server-to-server connections are allowed with the
|
91
120
|
# given domain.
|
92
121
|
def s2s?(domain)
|
93
|
-
@ports[:server] && @ports[:server].hosts.include?(domain)
|
122
|
+
@ports[:server] && @ports[:server].hosts.include?(domain.to_s)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Return true if the server is a member of a cluster, serving the same
|
126
|
+
# domains from different machines.
|
127
|
+
def cluster?
|
128
|
+
!!@cluster
|
94
129
|
end
|
95
130
|
|
96
131
|
# Retrieve the Port subclass with this name:
|
@@ -99,16 +134,16 @@ module Vines
|
|
99
134
|
@ports[name] or raise ArgumentError.new("no port named #{name}")
|
100
135
|
end
|
101
136
|
|
102
|
-
# Return true if the two
|
137
|
+
# Return true if the two JIDs are allowed to send messages to each other.
|
103
138
|
# Both domains must have enabled cross_domain_messages in their config files.
|
104
139
|
def allowed?(to, from)
|
105
140
|
to, from = JID.new(to), JID.new(from)
|
106
141
|
return false if to.empty? || from.empty?
|
107
142
|
return true if to.domain == from.domain # same domain always allowed
|
108
143
|
return cross_domain?(to, from) if local_jid?(to, from) # both virtual hosted here
|
109
|
-
return
|
110
|
-
return
|
111
|
-
return
|
144
|
+
return check_subdomains(to, from) if subdomain?(to, from) # component/pubsub to component/pubsub
|
145
|
+
return check_subdomain(to, from) if subdomain?(to) # to component/pubsub
|
146
|
+
return check_subdomain(from, to) if subdomain?(from) # from component/pubsub
|
112
147
|
return cross_domain?(to) if local_jid?(to) # from is remote
|
113
148
|
return cross_domain?(from) if local_jid?(from) # to is remote
|
114
149
|
return false
|
@@ -116,25 +151,44 @@ module Vines
|
|
116
151
|
|
117
152
|
private
|
118
153
|
|
119
|
-
|
120
|
-
|
121
|
-
|
154
|
+
# Return true if all of the JIDs are some kind of subdomain resource hosted
|
155
|
+
# here (either a component or a pubsub domain).
|
156
|
+
def subdomain?(*jids)
|
157
|
+
!jids.flatten.index do |jid|
|
158
|
+
!component?(jid) && !pubsub?(jid)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Return true if the third-level subdomain JIDs (components and pubsubs)
|
163
|
+
# are allowed to communicate with each other. For example, a
|
164
|
+
# tea.wonderland.lit component should be allowed to send messages to
|
165
|
+
# pubsub.wonderland.lit because they share the second-level wonderland.lit
|
166
|
+
# domain.
|
167
|
+
def check_subdomains(to, from)
|
168
|
+
sub1, sub2 = strip_domain(to), strip_domain(from)
|
169
|
+
(sub1 == sub2) || cross_domain?(sub1, sub2)
|
122
170
|
end
|
123
171
|
|
124
|
-
|
125
|
-
|
172
|
+
# Return true if the third-level subdomain JID (component or pubsub) is
|
173
|
+
# allowed to communicate with the other JID. For example,
|
174
|
+
# pubsub.wonderland.lit should be allowed to send messages to
|
175
|
+
# alice@wonderland.lit because they share the second-level wonderland.lit
|
176
|
+
# domain.
|
177
|
+
def check_subdomain(subdomain, jid)
|
178
|
+
comp = strip_domain(subdomain)
|
126
179
|
return true if comp.domain == jid.domain
|
127
180
|
local_jid?(jid) ? cross_domain?(comp, jid) : cross_domain?(comp)
|
128
181
|
end
|
129
182
|
|
130
|
-
# Return the JID's domain with the first subdomain stripped off
|
131
|
-
# alice@tea.wonderland.lit
|
183
|
+
# Return the third-level JID's domain with the first subdomain stripped off
|
184
|
+
# to create a second-level domain. For example, alice@tea.wonderland.lit
|
185
|
+
# returns wonderland.lit.
|
132
186
|
def strip_domain(jid)
|
133
187
|
domain = jid.domain.split('.').drop(1).join('.')
|
134
188
|
JID.new(domain)
|
135
189
|
end
|
136
190
|
|
137
|
-
# Return true if all
|
191
|
+
# Return true if all JIDs are allowed to exchange cross domain messages.
|
138
192
|
def cross_domain?(*jids)
|
139
193
|
!jids.flatten.index do |jid|
|
140
194
|
!@vhosts[jid.domain].cross_domain_messages?
|
data/lib/vines/jid.rb
CHANGED
@@ -30,14 +30,28 @@ module Vines
|
|
30
30
|
validate
|
31
31
|
end
|
32
32
|
|
33
|
+
# Strip the resource part from this JID and return it as a new
|
34
|
+
# JID object. The new JID contains only the optional node part
|
35
|
+
# and the required domain part from the original. This JID remains
|
36
|
+
# unchanged.
|
33
37
|
def bare
|
34
38
|
JID.new(@node, @domain)
|
35
39
|
end
|
36
40
|
|
41
|
+
# Return true if this is a bare JID without a resource part.
|
37
42
|
def bare?
|
38
43
|
@resource.nil?
|
39
44
|
end
|
40
45
|
|
46
|
+
# Return true if this is a domain-only JID without a node or resource part.
|
47
|
+
def domain?
|
48
|
+
!empty? && to_s == @domain
|
49
|
+
end
|
50
|
+
|
51
|
+
# Return true if this JID is equal to the empty string ''. That is, it's
|
52
|
+
# missing the node, domain, and resource parts that form a valid JID. It
|
53
|
+
# makes for easier error handling to be able to create JID objects from
|
54
|
+
# strings and then check if they're empty rather than nil.
|
41
55
|
def empty?
|
42
56
|
to_s == ''
|
43
57
|
end
|
data/lib/vines/router.rb
CHANGED
@@ -4,72 +4,74 @@ module Vines
|
|
4
4
|
# The router tracks all stream connections to the server for all clients,
|
5
5
|
# servers, and components. It sends stanzas to the correct stream based on
|
6
6
|
# the 'to' attribute. Router is a singleton, shared by all streams, that must
|
7
|
-
# be accessed with +
|
7
|
+
# be accessed with +Config#router+.
|
8
8
|
class Router
|
9
|
+
EMPTY = [].freeze
|
9
10
|
|
10
11
|
STREAM_TYPES = [:client, :server, :component].freeze
|
11
|
-
STREAM_TYPES.each do |name|
|
12
|
-
define_method "#{name}s" do
|
13
|
-
@streams[name]
|
14
|
-
end
|
15
|
-
end
|
16
12
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
def initialize
|
23
|
-
@config = nil
|
24
|
-
@streams = Hash.new {|h,k| h[k] = [] }
|
13
|
+
def initialize(config)
|
14
|
+
@config = config
|
15
|
+
@clients, @servers, @components = {}, [], []
|
25
16
|
@pending = Hash.new {|h,k| h[k] = [] }
|
26
17
|
end
|
27
18
|
|
28
|
-
# Returns streams for all connected resources for this JID. A
|
29
|
-
#
|
30
|
-
#
|
31
|
-
def connected_resources(jid, from)
|
19
|
+
# Returns streams for all connected resources for this JID. A resource is
|
20
|
+
# considered connected after it has completed authentication and resource
|
21
|
+
# binding.
|
22
|
+
def connected_resources(jid, from, proxies=true)
|
32
23
|
jid, from = JID.new(jid), JID.new(from)
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
24
|
+
return [] unless @config.allowed?(jid, from)
|
25
|
+
|
26
|
+
local = @clients[jid.bare] || EMPTY
|
27
|
+
local = local.select {|stream| stream.user.jid == jid } unless jid.bare?
|
28
|
+
remote = proxies ? proxies(jid) : EMPTY
|
29
|
+
[local, remote].flatten
|
38
30
|
end
|
39
31
|
|
40
|
-
# Returns streams for all available resources for this JID. A
|
41
|
-
#
|
42
|
-
# This method accepts a single JID or a list of JIDs.
|
32
|
+
# Returns streams for all available resources for this JID. A resource is
|
33
|
+
# marked available after it sends initial presence.
|
43
34
|
def available_resources(*jids, from)
|
44
|
-
|
45
|
-
|
46
|
-
stream.available? && jids.include?(stream.user.jid.bare)
|
35
|
+
clients(jids, from) do |stream|
|
36
|
+
stream.available?
|
47
37
|
end
|
48
38
|
end
|
49
39
|
|
50
|
-
# Returns streams for all interested resources for this JID. A
|
51
|
-
#
|
52
|
-
# This method accepts a single JID or a list of JIDs.
|
40
|
+
# Returns streams for all interested resources for this JID. A resource is
|
41
|
+
# marked interested after it requests the roster.
|
53
42
|
def interested_resources(*jids, from)
|
54
|
-
|
55
|
-
|
56
|
-
stream.interested? && jids.include?(stream.user.jid.bare)
|
43
|
+
clients(jids, from) do |stream|
|
44
|
+
stream.interested?
|
57
45
|
end
|
58
46
|
end
|
59
47
|
|
60
48
|
# Add the connection to the routing table. The connection must return
|
61
49
|
# :client, :server, or :component from its +stream_type+ method so the
|
62
50
|
# router can properly route stanzas to the stream.
|
63
|
-
def <<(
|
64
|
-
|
65
|
-
|
66
|
-
|
51
|
+
def <<(stream)
|
52
|
+
case stream_type(stream)
|
53
|
+
when :client then
|
54
|
+
return unless stream.connected?
|
55
|
+
jid = stream.user.jid.bare
|
56
|
+
@clients[jid] ||= []
|
57
|
+
@clients[jid] << stream
|
58
|
+
when :server then @servers << stream
|
59
|
+
when :component then @components << stream
|
60
|
+
end
|
67
61
|
end
|
68
62
|
|
69
63
|
# Remove the connection from the routing table.
|
70
|
-
def delete(
|
71
|
-
|
72
|
-
|
64
|
+
def delete(stream)
|
65
|
+
case stream_type(stream)
|
66
|
+
when :client then
|
67
|
+
return unless stream.connected?
|
68
|
+
jid = stream.user.jid.bare
|
69
|
+
streams = @clients[jid] || []
|
70
|
+
streams.delete(stream)
|
71
|
+
@clients.delete(jid) if streams.empty?
|
72
|
+
when :server then @servers.delete(stream)
|
73
|
+
when :component then @components.delete(stream)
|
74
|
+
end
|
73
75
|
end
|
74
76
|
|
75
77
|
# Send the stanza to the appropriate remote server-to-server stream
|
@@ -96,7 +98,8 @@ module Vines
|
|
96
98
|
|
97
99
|
# Returns the total number of streams connected to the server.
|
98
100
|
def size
|
99
|
-
@
|
101
|
+
clients = @clients.values.inject(0) {|sum, arr| sum + arr.size }
|
102
|
+
clients + @servers.size + @components.size
|
100
103
|
end
|
101
104
|
|
102
105
|
private
|
@@ -124,16 +127,27 @@ module Vines
|
|
124
127
|
end
|
125
128
|
end
|
126
129
|
|
127
|
-
# Return the
|
128
|
-
#
|
130
|
+
# Return the client streams to which the from address is allowed to
|
131
|
+
# contact. Apply the filter block to each stream to narrow the results
|
132
|
+
# before returning the streams.
|
133
|
+
def clients(jids, from, &filter)
|
134
|
+
jids = filter_allowed(jids, from)
|
135
|
+
local = @clients.values_at(*jids).compact.flatten.select(&filter)
|
136
|
+
proxies = proxies(*jids).select(&filter)
|
137
|
+
[local, proxies].flatten
|
138
|
+
end
|
139
|
+
|
140
|
+
# Return the bare JIDs from the list that are allowed to talk to
|
141
|
+
# the +from+ JID.
|
129
142
|
def filter_allowed(jids, from)
|
130
143
|
from = JID.new(from)
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
144
|
+
jids.flatten.map {|jid| JID.new(jid).bare }
|
145
|
+
.select {|jid| @config.allowed?(jid, from) }
|
146
|
+
end
|
147
|
+
|
148
|
+
def proxies(*jids)
|
149
|
+
return EMPTY unless @config.cluster?
|
150
|
+
@config.cluster.remote_sessions(*jids)
|
137
151
|
end
|
138
152
|
|
139
153
|
def connection_to(to, from)
|
@@ -141,17 +155,17 @@ module Vines
|
|
141
155
|
end
|
142
156
|
|
143
157
|
def component_stream(to)
|
144
|
-
components.
|
158
|
+
@components.select do |stream|
|
145
159
|
stream.ready? && stream.remote_domain == to.domain
|
146
|
-
end
|
160
|
+
end.sample
|
147
161
|
end
|
148
162
|
|
149
163
|
def server_stream(to, from)
|
150
|
-
servers.
|
164
|
+
@servers.select do |stream|
|
151
165
|
stream.ready? &&
|
152
166
|
stream.remote_domain == to.domain &&
|
153
167
|
stream.domain == from.domain
|
154
|
-
end
|
168
|
+
end.sample
|
155
169
|
end
|
156
170
|
|
157
171
|
def stream_type(connection)
|
@@ -9,23 +9,36 @@ module Vines
|
|
9
9
|
register "/iq[@id and @type='get']/ns:query", 'ns' => NS
|
10
10
|
|
11
11
|
def process
|
12
|
-
return if route_iq
|
12
|
+
return if route_iq || !allowed?
|
13
13
|
result = to_result.tap do |el|
|
14
14
|
el << el.document.create_element('query') do |query|
|
15
15
|
query.default_namespace = NS
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
if to_pubsub_domain?
|
17
|
+
identity(query, 'pubsub', 'service')
|
18
|
+
pubsub = [:pubsub_create, :pubsub_delete, :pubsub_instant, :pubsub_item_ids, :pubsub_publish, :pubsub_subscribe]
|
19
|
+
features(query, :disco_info, :ping, :pubsub, *pubsub)
|
20
|
+
else
|
21
|
+
identity(query, 'server', 'im')
|
22
|
+
features = [:disco_info, :disco_items, :ping, :vcard, :version]
|
23
|
+
features << :storage if stream.config.private_storage?(validate_to || stream.domain)
|
24
|
+
features(query, features)
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end
|
27
28
|
stream.write(result)
|
28
29
|
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def identity(query, category, type)
|
34
|
+
query << query.document.create_element('identity', 'category' => category, 'type' => type)
|
35
|
+
end
|
36
|
+
|
37
|
+
def features(query, *features)
|
38
|
+
features.flatten.each do |feature|
|
39
|
+
query << query.document.create_element('feature', 'var' => NAMESPACES[feature])
|
40
|
+
end
|
41
|
+
end
|
29
42
|
end
|
30
43
|
end
|
31
44
|
end
|
@@ -9,12 +9,15 @@ module Vines
|
|
9
9
|
register "/iq[@id and @type='get']/ns:query", 'ns' => NS
|
10
10
|
|
11
11
|
def process
|
12
|
-
return if route_iq
|
12
|
+
return if route_iq || !allowed?
|
13
13
|
result = to_result.tap do |el|
|
14
14
|
el << el.document.create_element('query') do |query|
|
15
15
|
query.default_namespace = NS
|
16
|
-
|
17
|
-
|
16
|
+
unless to_pubsub_domain?
|
17
|
+
to = (validate_to || stream.domain).to_s
|
18
|
+
stream.config.vhosts[to].disco_items.each do |domain|
|
19
|
+
query << el.document.create_element('item', 'jid' => domain)
|
20
|
+
end
|
18
21
|
end
|
19
22
|
end
|
20
23
|
end
|
data/lib/vines/stanza/iq/ping.rb
CHANGED
@@ -44,14 +44,10 @@ module Vines
|
|
44
44
|
private
|
45
45
|
|
46
46
|
def to_result
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
'to' => stream.user.jid.to_s,
|
52
|
-
'type' => 'result')
|
53
|
-
yield node if block_given?
|
54
|
-
node
|
47
|
+
super.tap do |node|
|
48
|
+
node['from'] = stream.user.jid.to_s
|
49
|
+
yield node if block_given?
|
50
|
+
end
|
55
51
|
end
|
56
52
|
|
57
53
|
def validate_children_size
|
@@ -55,7 +55,7 @@ module Vines
|
|
55
55
|
|
56
56
|
contact = stream.user.contact(jid)
|
57
57
|
unless contact
|
58
|
-
contact = Contact.new(:
|
58
|
+
contact = Contact.new(jid: jid)
|
59
59
|
stream.user.roster << contact
|
60
60
|
end
|
61
61
|
contact.name = item['name']
|
@@ -89,9 +89,8 @@ module Vines
|
|
89
89
|
end
|
90
90
|
|
91
91
|
send_result_iq
|
92
|
-
push_roster_updates(stream.user.jid,
|
93
|
-
:
|
94
|
-
:subscription => 'remove'))
|
92
|
+
push_roster_updates(stream.user.jid,
|
93
|
+
Contact.new(jid: contact.jid, subscription: 'remove'))
|
95
94
|
|
96
95
|
if local_jid?(contact.jid)
|
97
96
|
send_unavailable(stream.user.jid, contact.jid) if contact.subscribed_from?
|
@@ -110,14 +109,7 @@ module Vines
|
|
110
109
|
presence = [%w[to unsubscribe], %w[from unsubscribed]].map do |meth, type|
|
111
110
|
presence(contact.jid, type) if contact.send("subscribed_#{meth}?")
|
112
111
|
end.compact
|
113
|
-
|
114
|
-
if local_jid?(contact.jid)
|
115
|
-
stream.interested_resources(contact.jid).each do |recipient|
|
116
|
-
presence.each {|el| recipient.write(el) }
|
117
|
-
end
|
118
|
-
else
|
119
|
-
presence.each {|el| router.route(el) }
|
120
|
-
end
|
112
|
+
broadcast_to_interested_resources(presence, contact.jid)
|
121
113
|
end
|
122
114
|
|
123
115
|
def presence(to, type)
|
@@ -138,8 +130,8 @@ module Vines
|
|
138
130
|
end
|
139
131
|
|
140
132
|
def send_result_iq
|
141
|
-
|
142
|
-
node
|
133
|
+
node = to_result
|
134
|
+
node.remove_attribute('from')
|
143
135
|
stream.write(node)
|
144
136
|
end
|
145
137
|
end
|
@@ -3,18 +3,13 @@
|
|
3
3
|
module Vines
|
4
4
|
class Stanza
|
5
5
|
class Iq
|
6
|
-
# Session support is deprecated, but Adium requires it so reply with an
|
6
|
+
# Session support is deprecated, but Adium requires it, so reply with an
|
7
7
|
# iq result stanza.
|
8
8
|
class Session < Iq
|
9
9
|
register "/iq[@id and @type='set']/ns:session", 'ns' => NAMESPACES[:session]
|
10
10
|
|
11
11
|
def process
|
12
|
-
|
13
|
-
result = doc.create_element('iq',
|
14
|
-
'from' => stream.domain,
|
15
|
-
'id' => self['id'],
|
16
|
-
'type' => 'result')
|
17
|
-
stream.write(result)
|
12
|
+
stream.write(to_result)
|
18
13
|
end
|
19
14
|
end
|
20
15
|
end
|
@@ -9,6 +9,7 @@ module Vines
|
|
9
9
|
register "/iq[@id and @type='get' or @type='set']/ns:vCard", 'ns' => NS
|
10
10
|
|
11
11
|
def process
|
12
|
+
return unless allowed?
|
12
13
|
if local?
|
13
14
|
get? ? vcard_query : vcard_update
|
14
15
|
else
|
@@ -22,7 +23,7 @@ module Vines
|
|
22
23
|
def vcard_query
|
23
24
|
to = validate_to
|
24
25
|
jid = to ? to.bare : stream.user.jid.bare
|
25
|
-
card = storage.find_vcard(jid)
|
26
|
+
card = storage(jid.domain).find_vcard(jid)
|
26
27
|
|
27
28
|
raise StanzaErrors::ItemNotFound.new(self, 'cancel') unless card
|
28
29
|
|
@@ -45,11 +46,8 @@ module Vines
|
|
45
46
|
|
46
47
|
storage.save_vcard(stream.user.jid, elements.first)
|
47
48
|
|
48
|
-
|
49
|
-
result
|
50
|
-
'id' => self['id'],
|
51
|
-
'to' => stream.user.jid.to_s,
|
52
|
-
'type' => 'result')
|
49
|
+
result = to_result
|
50
|
+
result.remove_attribute('from')
|
53
51
|
stream.write(result)
|
54
52
|
end
|
55
53
|
end
|
@@ -9,7 +9,7 @@ module Vines
|
|
9
9
|
register "/iq[@id and @type='get']/ns:query", 'ns' => NS
|
10
10
|
|
11
11
|
def process
|
12
|
-
return if route_iq
|
12
|
+
return if route_iq || to_pubsub_domain? || !allowed?
|
13
13
|
result = to_result.tap do |node|
|
14
14
|
node << node.document.create_element('query') do |query|
|
15
15
|
query.default_namespace = NS
|
data/lib/vines/stanza/iq.rb
CHANGED
@@ -24,25 +24,23 @@ module Vines
|
|
24
24
|
def to_result
|
25
25
|
doc = Document.new
|
26
26
|
doc.create_element('iq',
|
27
|
-
'from' => stream.domain,
|
27
|
+
'from' => validate_to || stream.domain,
|
28
28
|
'id' => self['id'],
|
29
|
-
'to' => stream.user.jid
|
29
|
+
'to' => stream.user.jid,
|
30
30
|
'type' => 'result')
|
31
31
|
end
|
32
32
|
|
33
33
|
private
|
34
34
|
|
35
|
+
# Return false if this IQ stanza is addressed to the server, or a pubsub
|
36
|
+
# service hosted here, and must be handled locally. Return true if the
|
37
|
+
# stanza must not be handled locally and has been routed to the appropriate
|
38
|
+
# component, s2s, or c2s stream.
|
35
39
|
def route_iq
|
36
40
|
to = validate_to
|
37
|
-
return false if to.nil? || to
|
41
|
+
return false if to.nil? || stream.config.vhost?(to) || to_pubsub_domain?
|
38
42
|
self['from'] = stream.user.jid.to_s
|
39
|
-
|
40
|
-
stream.available_resources(to).each do |recipient|
|
41
|
-
recipient.write(@node)
|
42
|
-
end
|
43
|
-
else
|
44
|
-
route
|
45
|
-
end
|
43
|
+
local? ? broadcast(stream.connected_resources(to)) : route
|
46
44
|
true
|
47
45
|
end
|
48
46
|
end
|