vines 0.3.2 → 0.4.0
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/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
@@ -7,29 +7,21 @@ module Vines
|
|
7
7
|
register "/presence[@type='subscribe']"
|
8
8
|
|
9
9
|
def process
|
10
|
+
stamp_from
|
10
11
|
inbound? ? process_inbound : process_outbound
|
11
12
|
end
|
12
13
|
|
13
14
|
def process_outbound
|
14
|
-
self['from'] = stream.user.jid.bare.to_s
|
15
15
|
to = stamp_to
|
16
|
-
|
17
16
|
stream.user.request_subscription(to)
|
18
17
|
storage.save_user(stream.user)
|
19
18
|
stream.update_user_streams(stream.user)
|
20
|
-
|
21
19
|
local? ? process_inbound : route
|
22
|
-
|
23
|
-
contact = stream.user.contact(to)
|
24
|
-
stream.interested_resources(stream.user.jid).each do |recipient|
|
25
|
-
contact.send_roster_push(recipient)
|
26
|
-
end
|
20
|
+
send_roster_push(to)
|
27
21
|
end
|
28
22
|
|
29
23
|
def process_inbound
|
30
|
-
self['from'] = stream.user.jid.bare.to_s
|
31
24
|
to = stamp_to
|
32
|
-
|
33
25
|
contact = storage(to.domain).find_user(to)
|
34
26
|
if contact.nil?
|
35
27
|
auto_reply_to_subscription_request(to, 'unsubscribed')
|
@@ -40,7 +32,7 @@ module Vines
|
|
40
32
|
if recipients.empty?
|
41
33
|
# TODO store subscription request per RFC 6121 3.1.3 #4
|
42
34
|
else
|
43
|
-
|
35
|
+
broadcast_to_available_resources([@node], to)
|
44
36
|
end
|
45
37
|
end
|
46
38
|
end
|
@@ -7,56 +7,43 @@ module Vines
|
|
7
7
|
register "/presence[@type='subscribed']"
|
8
8
|
|
9
9
|
def process
|
10
|
+
stamp_from
|
10
11
|
inbound? ? process_inbound : process_outbound
|
11
12
|
end
|
12
13
|
|
13
14
|
def process_outbound
|
14
|
-
self['from'] = stream.user.jid.bare.to_s
|
15
15
|
to = stamp_to
|
16
|
-
|
17
16
|
stream.user.add_subscription_from(to)
|
18
17
|
storage.save_user(stream.user)
|
19
18
|
stream.update_user_streams(stream.user)
|
20
|
-
|
21
19
|
local? ? process_inbound : route
|
22
|
-
|
23
|
-
|
24
|
-
stream.interested_resources(stream.user.jid).each do |recipient|
|
25
|
-
contact.send_roster_push(recipient)
|
26
|
-
end
|
27
|
-
|
28
|
-
presences = stream.available_resources(stream.user.jid).map do |c|
|
29
|
-
c.last_broadcast_presence.clone.tap do |node|
|
30
|
-
node['from'] = c.user.jid.to_s
|
31
|
-
node['id'] = Kit.uuid
|
32
|
-
node['to'] = to.to_s
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
if local?
|
37
|
-
stream.available_resources(to).each do |recipient|
|
38
|
-
presences.each {|el| recipient.write(el) }
|
39
|
-
end
|
40
|
-
else
|
41
|
-
presences.each {|el| router.route(el) }
|
42
|
-
end
|
20
|
+
send_roster_push(to)
|
21
|
+
send_known_presence(to)
|
43
22
|
end
|
44
23
|
|
45
24
|
def process_inbound
|
46
|
-
self['from'] = stream.user.jid.bare.to_s
|
47
25
|
to = stamp_to
|
48
|
-
|
49
26
|
user = storage(to.domain).find_user(to)
|
50
27
|
contact = user.contact(stream.user.jid) if user
|
51
28
|
return unless contact && contact.can_subscribe?
|
52
29
|
contact.subscribe_to
|
53
30
|
storage(to.domain).save_user(user)
|
54
31
|
stream.update_user_streams(user)
|
32
|
+
broadcast_subscription_change(contact)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
55
36
|
|
56
|
-
|
57
|
-
|
58
|
-
|
37
|
+
# After approving a contact's subscription to this user's presence,
|
38
|
+
# broadcast this user's most recent presence stanzas to the contact.
|
39
|
+
def send_known_presence(to)
|
40
|
+
stanzas = stream.available_resources(stream.user.jid).map do |stream|
|
41
|
+
stream.last_broadcast_presence.clone.tap do |node|
|
42
|
+
node['from'] = stream.user.jid.to_s
|
43
|
+
node['id'] = Kit.uuid
|
44
|
+
end
|
59
45
|
end
|
46
|
+
broadcast_to_available_resources(stanzas, to)
|
60
47
|
end
|
61
48
|
end
|
62
49
|
end
|
@@ -7,41 +7,29 @@ module Vines
|
|
7
7
|
register "/presence[@type='unsubscribe']"
|
8
8
|
|
9
9
|
def process
|
10
|
+
stamp_from
|
10
11
|
inbound? ? process_inbound : process_outbound
|
11
12
|
end
|
12
13
|
|
13
14
|
def process_outbound
|
14
|
-
self['from'] = stream.user.jid.bare.to_s
|
15
15
|
to = stamp_to
|
16
|
-
|
17
16
|
return unless stream.user.subscribed_to?(to)
|
18
17
|
stream.user.remove_subscription_to(to)
|
19
18
|
storage.save_user(stream.user)
|
20
19
|
stream.update_user_streams(stream.user)
|
21
|
-
|
22
20
|
local? ? process_inbound : route
|
23
|
-
|
24
|
-
contact = stream.user.contact(to)
|
25
|
-
stream.interested_resources(stream.user.jid).each do |recipient|
|
26
|
-
contact.send_roster_push(recipient)
|
27
|
-
end
|
21
|
+
send_roster_push(to)
|
28
22
|
end
|
29
23
|
|
30
24
|
def process_inbound
|
31
|
-
self['from'] = stream.user.jid.bare.to_s
|
32
25
|
to = stamp_to
|
33
|
-
|
34
26
|
user = storage(to.domain).find_user(to)
|
35
27
|
return unless user && user.subscribed_from?(stream.user.jid)
|
36
28
|
contact = user.contact(stream.user.jid)
|
37
29
|
contact.unsubscribe_from
|
38
30
|
storage(to.domain).save_user(user)
|
39
31
|
stream.update_user_streams(user)
|
40
|
-
|
41
|
-
stream.interested_resources(to).each do |recipient|
|
42
|
-
recipient.write(@node)
|
43
|
-
contact.send_roster_push(recipient)
|
44
|
-
end
|
32
|
+
broadcast_subscription_change(contact)
|
45
33
|
send_unavailable(to, stream.user.jid.bare)
|
46
34
|
end
|
47
35
|
end
|
@@ -7,43 +7,30 @@ module Vines
|
|
7
7
|
register "/presence[@type='unsubscribed']"
|
8
8
|
|
9
9
|
def process
|
10
|
+
stamp_from
|
10
11
|
inbound? ? process_inbound : process_outbound
|
11
12
|
end
|
12
13
|
|
13
14
|
def process_outbound
|
14
|
-
self['from'] = stream.user.jid.bare.to_s
|
15
15
|
to = stamp_to
|
16
|
-
|
17
16
|
return unless stream.user.subscribed_from?(to)
|
18
17
|
send_unavailable(stream.user.jid, to)
|
19
|
-
|
20
18
|
stream.user.remove_subscription_from(to)
|
21
19
|
storage.save_user(stream.user)
|
22
20
|
stream.update_user_streams(stream.user)
|
23
|
-
|
24
21
|
local? ? process_inbound : route
|
25
|
-
|
26
|
-
contact = stream.user.contact(to)
|
27
|
-
stream.interested_resources(stream.user.jid).each do |recipient|
|
28
|
-
contact.send_roster_push(recipient)
|
29
|
-
end
|
22
|
+
send_roster_push(to)
|
30
23
|
end
|
31
24
|
|
32
25
|
def process_inbound
|
33
|
-
self['from'] = stream.user.jid.bare.to_s
|
34
26
|
to = stamp_to
|
35
|
-
|
36
27
|
user = storage(to.domain).find_user(to)
|
37
28
|
return unless user && user.subscribed_to?(stream.user.jid)
|
38
29
|
contact = user.contact(stream.user.jid)
|
39
30
|
contact.unsubscribe_to
|
40
31
|
storage(to.domain).save_user(user)
|
41
32
|
stream.update_user_streams(user)
|
42
|
-
|
43
|
-
stream.interested_resources(to).each do |recipient|
|
44
|
-
recipient.write(@node)
|
45
|
-
contact.send_roster_push(recipient)
|
46
|
-
end
|
33
|
+
broadcast_subscription_change(contact)
|
47
34
|
end
|
48
35
|
end
|
49
36
|
end
|
@@ -96,6 +96,28 @@ module Vines
|
|
96
96
|
stream.write(node)
|
97
97
|
end
|
98
98
|
|
99
|
+
# Send the contact's roster item to the current user's interested streams.
|
100
|
+
# Roster pushes are required, following presence subscription updates, to
|
101
|
+
# notify the user's clients of the contact's current state.
|
102
|
+
def send_roster_push(to)
|
103
|
+
contact = stream.user.contact(to)
|
104
|
+
stream.interested_resources(stream.user.jid).each do |recipient|
|
105
|
+
contact.send_roster_push(recipient)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Notify the current user's interested streams of a contact's subscription
|
110
|
+
# state change as a result of receiving a subscribed, unsubscribe, or
|
111
|
+
# unsubscribed presence stanza.
|
112
|
+
def broadcast_subscription_change(contact)
|
113
|
+
stamp_from
|
114
|
+
stream.interested_resources(stamp_to).each do |recipient|
|
115
|
+
@node['to'] = recipient.user.jid.to_s
|
116
|
+
recipient.write(@node)
|
117
|
+
contact.send_roster_push(recipient)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
99
121
|
# Validate that the incoming stanza has a 'to' attribute and strip any
|
100
122
|
# resource part from it so it's a bare jid. Return the bare JID object
|
101
123
|
# that was stamped.
|
@@ -106,6 +128,14 @@ module Vines
|
|
106
128
|
self['to'] = bare.to_s
|
107
129
|
end
|
108
130
|
end
|
131
|
+
|
132
|
+
# Presence subscription stanzas must be addressed from the user's bare
|
133
|
+
# JID. Return the user's bare JID object that was stamped.
|
134
|
+
def stamp_from
|
135
|
+
stream.user.jid.bare.tap do |bare|
|
136
|
+
self['from'] = bare.to_s
|
137
|
+
end
|
138
|
+
end
|
109
139
|
end
|
110
140
|
end
|
111
141
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class PubSub
|
6
|
+
class Create < PubSub
|
7
|
+
NS = NAMESPACES[:pubsub]
|
8
|
+
|
9
|
+
register "/iq[@id and @type='set']/ns:pubsub/ns:create", 'ns' => NS
|
10
|
+
|
11
|
+
def process
|
12
|
+
return if route_iq || !allowed?
|
13
|
+
validate_to_address
|
14
|
+
|
15
|
+
node = self.xpath('ns:pubsub/ns:create', 'ns' => NS)
|
16
|
+
raise StanzaErrors::BadRequest.new(self, 'modify') if node.size != 1
|
17
|
+
node = node.first
|
18
|
+
|
19
|
+
id = (node['node'] || '').strip
|
20
|
+
id = Kit.uuid if id.empty?
|
21
|
+
raise StanzaErrors::Conflict.new(self, 'cancel') if pubsub.node?(id)
|
22
|
+
pubsub.add_node(id)
|
23
|
+
send_result_iq(id)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def send_result_iq(id)
|
29
|
+
el = to_result
|
30
|
+
el << el.document.create_element('pubsub') do |node|
|
31
|
+
node.default_namespace = NS
|
32
|
+
node << el.document.create_element('create', 'node' => id)
|
33
|
+
end
|
34
|
+
stream.write(el)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class PubSub
|
6
|
+
class Delete < PubSub
|
7
|
+
NS = NAMESPACES[:pubsub]
|
8
|
+
|
9
|
+
register "/iq[@id and @type='set']/ns:pubsub/ns:delete", 'ns' => NS
|
10
|
+
|
11
|
+
def process
|
12
|
+
return if route_iq || !allowed?
|
13
|
+
validate_to_address
|
14
|
+
|
15
|
+
node = self.xpath('ns:pubsub/ns:delete', 'ns' => NS)
|
16
|
+
raise StanzaErrors::BadRequest.new(self, 'modify') if node.size != 1
|
17
|
+
node = node.first
|
18
|
+
|
19
|
+
id = node['node']
|
20
|
+
raise StanzaErrors::ItemNotFound.new(self, 'cancel') unless pubsub.node?(id)
|
21
|
+
|
22
|
+
pubsub.publish(id, message(id))
|
23
|
+
pubsub.delete_node(id)
|
24
|
+
stream.write(to_result)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def message(id)
|
30
|
+
doc = Document.new
|
31
|
+
doc.create_element('message') do |node|
|
32
|
+
node << node.document.create_element('event') do |event|
|
33
|
+
event.default_namespace = NAMESPACES[:pubsub_event]
|
34
|
+
event << node.document.create_element('delete', 'node' => id)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class PubSub
|
6
|
+
class Publish < PubSub
|
7
|
+
NS = NAMESPACES[:pubsub]
|
8
|
+
|
9
|
+
register "/iq[@id and @type='set']/ns:pubsub/ns:publish", 'ns' => NS
|
10
|
+
|
11
|
+
def process
|
12
|
+
return if route_iq || !allowed?
|
13
|
+
validate_to_address
|
14
|
+
|
15
|
+
node = self.xpath('ns:pubsub/ns:publish', 'ns' => NS)
|
16
|
+
raise StanzaErrors::BadRequest.new(self, 'modify') if node.size != 1
|
17
|
+
node = node.first
|
18
|
+
id = node['node']
|
19
|
+
|
20
|
+
raise StanzaErrors::ItemNotFound.new(self, 'cancel') unless pubsub.node?(id)
|
21
|
+
|
22
|
+
item = node.xpath('ns:item', 'ns' => NS)
|
23
|
+
raise StanzaErrors::BadRequest.new(self, 'modify') unless item.size == 1
|
24
|
+
item = item.first
|
25
|
+
unless item['id']
|
26
|
+
item['id'] = Kit.uuid
|
27
|
+
include_item = true
|
28
|
+
end
|
29
|
+
|
30
|
+
raise StanzaErrors::BadRequest.new(self, 'modify') unless item.elements.size == 1
|
31
|
+
pubsub.publish(id, message(id, item))
|
32
|
+
send_result_iq(id, include_item ? item : nil)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def message(node, item)
|
38
|
+
doc = Document.new
|
39
|
+
doc.create_element('message') do |message|
|
40
|
+
message << doc.create_element('event') do |event|
|
41
|
+
event.default_namespace = NAMESPACES[:pubsub_event]
|
42
|
+
event << doc.create_element('items', 'node' => node) do |items|
|
43
|
+
items << doc.create_element('item', 'id' => item['id'], 'publisher' => stream.user.jid.to_s) do |el|
|
44
|
+
el << item.elements.first
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def send_result_iq(node, item)
|
52
|
+
result = to_result
|
53
|
+
if item
|
54
|
+
result << result.document.create_element('pubsub') do |pubsub|
|
55
|
+
pubsub.default_namespace = NS
|
56
|
+
pubsub << result.document.create_element('publish', 'node' => node) do |publish|
|
57
|
+
publish << result.document.create_element('item', 'id' => item['id'])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
stream.write(result)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class PubSub
|
6
|
+
class Subscribe < PubSub
|
7
|
+
NS = NAMESPACES[:pubsub]
|
8
|
+
|
9
|
+
register "/iq[@id and @type='set']/ns:pubsub/ns:subscribe", 'ns' => NS
|
10
|
+
|
11
|
+
def process
|
12
|
+
return if route_iq || !allowed?
|
13
|
+
validate_to_address
|
14
|
+
|
15
|
+
node = self.xpath('ns:pubsub/ns:subscribe', 'ns' => NS)
|
16
|
+
raise StanzaErrors::BadRequest.new(self, 'modify') if node.size != 1
|
17
|
+
node = node.first
|
18
|
+
id, jid = node['node'], JID.new(node['jid'])
|
19
|
+
|
20
|
+
raise StanzaErrors::BadRequest.new(self, 'modify') unless stream.user.jid.bare == jid.bare
|
21
|
+
raise StanzaErrors::ItemNotFound.new(self, 'cancel') unless pubsub.node?(id)
|
22
|
+
raise StanzaErrors::PolicyViolation.new(self, 'wait') if pubsub.subscribed?(id, jid)
|
23
|
+
|
24
|
+
pubsub.subscribe(id, jid)
|
25
|
+
send_result_iq(id, jid)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def send_result_iq(id, jid)
|
31
|
+
result = to_result
|
32
|
+
result << result.document.create_element('pubsub') do |node|
|
33
|
+
node.default_namespace = NS
|
34
|
+
node << result.document.create_element('subscription',
|
35
|
+
'node' => id,
|
36
|
+
'jid' => jid.to_s,
|
37
|
+
'subscription' => 'subscribed')
|
38
|
+
end
|
39
|
+
stream.write(result)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class PubSub
|
6
|
+
class Unsubscribe < PubSub
|
7
|
+
NS = NAMESPACES[:pubsub]
|
8
|
+
|
9
|
+
register "/iq[@id and @type='set']/ns:pubsub/ns:unsubscribe", 'ns' => NS
|
10
|
+
|
11
|
+
def process
|
12
|
+
return if route_iq || !allowed?
|
13
|
+
validate_to_address
|
14
|
+
|
15
|
+
node = self.xpath('ns:pubsub/ns:unsubscribe', 'ns' => NS)
|
16
|
+
raise StanzaErrors::BadRequest.new(self, 'modify') if node.size != 1
|
17
|
+
node = node.first
|
18
|
+
id, jid = node['node'], JID.new(node['jid'])
|
19
|
+
|
20
|
+
raise StanzaErrors::Forbidden.new(self, 'auth') unless stream.user.jid.bare == jid.bare
|
21
|
+
raise StanzaErrors::ItemNotFound.new(self, 'cancel') unless pubsub.node?(id)
|
22
|
+
raise StanzaErrors::UnexpectedRequest.new(self, 'cancel') unless pubsub.subscribed?(id, jid)
|
23
|
+
|
24
|
+
pubsub.unsubscribe(id, jid)
|
25
|
+
stream.write(to_result)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class PubSub < Iq
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
# Return the Config::PubSub system for the domain to which this stanza is
|
10
|
+
# addressed or nil if it's not to a pubsub subdomain.
|
11
|
+
def pubsub
|
12
|
+
stream.config.pubsub(validate_to)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Raise feature-not-implemented if this stanza is addressed to the chat
|
16
|
+
# server itself, rather than a pubsub subdomain.
|
17
|
+
def validate_to_address
|
18
|
+
raise StanzaErrors::FeatureNotImplemented.new(self, 'cancel') unless pubsub
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/vines/stanza.rb
CHANGED
@@ -6,12 +6,9 @@ module Vines
|
|
6
6
|
|
7
7
|
attr_reader :stream
|
8
8
|
|
9
|
-
EMPTY
|
10
|
-
FROM
|
11
|
-
|
12
|
-
TO = 'to'.freeze
|
13
|
-
|
14
|
-
ROUTABLE_STANZAS = %w[message iq presence].freeze
|
9
|
+
EMPTY = ''.freeze
|
10
|
+
FROM, MESSAGE, TO = %w[from message to].map {|s| s.freeze }
|
11
|
+
ROUTABLE_STANZAS = %w[message iq presence].freeze
|
15
12
|
|
16
13
|
@@types = {}
|
17
14
|
|
@@ -45,7 +42,7 @@ module Vines
|
|
45
42
|
# if it's destined for a remote domain or external component.
|
46
43
|
def local?
|
47
44
|
return true unless ROUTABLE_STANZAS.include?(@node.name)
|
48
|
-
to = JID.new(@node[
|
45
|
+
to = JID.new(@node[TO])
|
49
46
|
to.empty? || local_jid?(to)
|
50
47
|
end
|
51
48
|
|
@@ -53,6 +50,14 @@ module Vines
|
|
53
50
|
stream.config.local_jid?(*jids)
|
54
51
|
end
|
55
52
|
|
53
|
+
# Return true if this stanza is addressed to a pubsub subdomain hosted
|
54
|
+
# at this server. This helps differentiate between IQ stanzas addressed
|
55
|
+
# to the server and stanzas addressed to pubsub domains, both of which must
|
56
|
+
# be handled locally and not routed.
|
57
|
+
def to_pubsub_domain?
|
58
|
+
stream.config.pubsub?(validate_to)
|
59
|
+
end
|
60
|
+
|
56
61
|
def route
|
57
62
|
stream.router.route(@node)
|
58
63
|
end
|
@@ -73,25 +78,17 @@ module Vines
|
|
73
78
|
# recipient's available resources. Route the stanza to a remote server if
|
74
79
|
# the recipient isn't hosted locally.
|
75
80
|
def send_unavailable(from, to)
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
el = unavailable(stream.user.jid, to)
|
80
|
-
if local_jid?(to)
|
81
|
-
recipients.each {|recipient| recipient.write(el) }
|
82
|
-
else
|
83
|
-
router.route(el)
|
84
|
-
end
|
85
|
-
end
|
81
|
+
available = router.available_resources(from, to)
|
82
|
+
stanzas = available.map {|stream| unavailable(stream.user.jid) }
|
83
|
+
broadcast_to_available_resources(stanzas, to)
|
86
84
|
end
|
87
85
|
|
88
|
-
# Return an unavailable presence stanza addressed
|
89
|
-
def unavailable(from
|
86
|
+
# Return an unavailable presence stanza addressed from the given JID.
|
87
|
+
def unavailable(from)
|
90
88
|
doc = Document.new
|
91
89
|
doc.create_element('presence',
|
92
90
|
'from' => from.to_s,
|
93
91
|
'id' => Kit.uuid,
|
94
|
-
'to' => to.to_s,
|
95
92
|
'type' => 'unavailable')
|
96
93
|
end
|
97
94
|
|
@@ -115,11 +112,64 @@ module Vines
|
|
115
112
|
|
116
113
|
private
|
117
114
|
|
115
|
+
# Send the stanzas to the destination JID, routing to a s2s stream
|
116
|
+
# if the address is remote. This method properly stamps the to address
|
117
|
+
# on each stanza before it's sent. The caller must set the from address.
|
118
|
+
def broadcast_to_available_resources(stanzas, to)
|
119
|
+
return if send_to_remote(stanzas, to)
|
120
|
+
send_to_recipients(stanzas, stream.available_resources(to))
|
121
|
+
end
|
122
|
+
|
123
|
+
# Send the stanzas to the destination JID, routing to a s2s stream
|
124
|
+
# if the address is remote. This method properly stamps the to address
|
125
|
+
# on each stanza before it's sent. The caller must set the from address.
|
126
|
+
def broadcast_to_interested_resources(stanzas, to)
|
127
|
+
return if send_to_remote(stanzas, to)
|
128
|
+
send_to_recipients(stanzas, stream.interested_resources(to))
|
129
|
+
end
|
130
|
+
|
131
|
+
# Route the stanzas to a remote server, stamping a bare JID as the
|
132
|
+
# to address. Bare JIDs are required for presence subscription stanzas
|
133
|
+
# sent to the remote contact's server. Return true if the stanzas were
|
134
|
+
# routed, false if they must be delivered locally.
|
135
|
+
def send_to_remote(stanzas, to)
|
136
|
+
return false if local_jid?(to)
|
137
|
+
to = JID.new(to)
|
138
|
+
stanzas.each do |el|
|
139
|
+
el[TO] = to.bare.to_s
|
140
|
+
router.route(el)
|
141
|
+
end
|
142
|
+
true
|
143
|
+
end
|
144
|
+
|
145
|
+
# Send the stanzas to the local recipient streams, stamping a full JID as
|
146
|
+
# the to address. It's important to use full JIDs, even when sending to
|
147
|
+
# local clients, because the stanzas may be routed to other cluster nodes
|
148
|
+
# for delivery. We need the receiving cluster node to send the stanza just
|
149
|
+
# to this full JID, not to lookup all JIDs for this user.
|
150
|
+
def send_to_recipients(stanzas, recipients)
|
151
|
+
recipients.each do |recipient|
|
152
|
+
stanzas.each do |el|
|
153
|
+
el[TO] = recipient.user.jid.to_s
|
154
|
+
recipient.write(el)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Return true if the to and from JIDs are allowed to communicate with one
|
160
|
+
# another based on the cross_domain_messages setting in conf/config.rb. If
|
161
|
+
# a domain's users are isolated to sending messages only within their own
|
162
|
+
# domain, pubsub stanzas must not be processed from remote JIDs.
|
163
|
+
def allowed?
|
164
|
+
stream.config.allowed?(validate_to || stream.domain, stream.user.jid)
|
165
|
+
end
|
166
|
+
|
118
167
|
def validate_address(attr)
|
119
168
|
jid = (self[attr] || EMPTY)
|
120
169
|
return if jid.empty?
|
121
|
-
JID.new(jid)
|
122
|
-
|
170
|
+
JID.new(jid)
|
171
|
+
rescue
|
172
|
+
raise StanzaErrors::JidMalformed.new(self, 'modify')
|
123
173
|
end
|
124
174
|
end
|
125
175
|
end
|