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,17 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Iq
|
6
|
+
# Session support is deprecated, but Adium requires it, so reply with an
|
7
|
+
# iq result stanza.
|
8
|
+
class Session < Iq
|
9
|
+
register "/iq[@id and @type='set']/ns:session", 'ns' => NAMESPACES[:session]
|
10
|
+
|
11
|
+
def process
|
12
|
+
stream.write(to_result)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Iq
|
6
|
+
class Vcard < Iq
|
7
|
+
NS = NAMESPACES[:vcard]
|
8
|
+
|
9
|
+
register "/iq[@id and @type='get' or @type='set']/ns:vCard", 'ns' => NS
|
10
|
+
|
11
|
+
def process
|
12
|
+
return unless allowed?
|
13
|
+
if local?
|
14
|
+
get? ? vcard_query : vcard_update
|
15
|
+
else
|
16
|
+
self['from'] = stream.user.jid.to_s
|
17
|
+
route
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def vcard_query
|
24
|
+
to = validate_to
|
25
|
+
jid = to ? to.bare : stream.user.jid.bare
|
26
|
+
card = storage(jid.domain).find_vcard(jid)
|
27
|
+
|
28
|
+
raise StanzaErrors::ItemNotFound.new(self, 'cancel') unless card
|
29
|
+
|
30
|
+
doc = Document.new
|
31
|
+
result = doc.create_element('iq') do |node|
|
32
|
+
node['from'] = jid.to_s unless jid == stream.user.jid.bare
|
33
|
+
node['id'] = self['id']
|
34
|
+
node['to'] = stream.user.jid.to_s
|
35
|
+
node['type'] = 'result'
|
36
|
+
node << card
|
37
|
+
end
|
38
|
+
stream.write(result)
|
39
|
+
end
|
40
|
+
|
41
|
+
def vcard_update
|
42
|
+
to = validate_to
|
43
|
+
unless to.nil? || to == stream.user.jid.bare
|
44
|
+
raise StanzaErrors::Forbidden.new(self, 'auth')
|
45
|
+
end
|
46
|
+
|
47
|
+
storage.save_vcard(stream.user.jid, elements.first)
|
48
|
+
|
49
|
+
result = to_result
|
50
|
+
result.remove_attribute('from')
|
51
|
+
stream.write(result)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Iq
|
6
|
+
class Version < Query
|
7
|
+
NS = NAMESPACES[:version]
|
8
|
+
|
9
|
+
register "/iq[@id and @type='get']/ns:query", 'ns' => NS
|
10
|
+
|
11
|
+
def process
|
12
|
+
return if route_iq || to_pubsub_domain? || !allowed?
|
13
|
+
result = to_result.tap do |node|
|
14
|
+
node << node.document.create_element('query') do |query|
|
15
|
+
query.default_namespace = NS
|
16
|
+
query << node.document.create_element('name', 'Vines')
|
17
|
+
query << node.document.create_element('version', VERSION)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
stream.write(result)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Message < Stanza
|
6
|
+
register "/message"
|
7
|
+
|
8
|
+
TYPE, FROM = %w[type from].map {|s| s.freeze }
|
9
|
+
VALID_TYPES = %w[chat error groupchat headline normal].freeze
|
10
|
+
|
11
|
+
VALID_TYPES.each do |type|
|
12
|
+
define_method "#{type}?" do
|
13
|
+
self[TYPE] == type
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def process
|
18
|
+
unless self[TYPE].nil? || VALID_TYPES.include?(self[TYPE])
|
19
|
+
raise StanzaErrors::BadRequest.new(self, 'modify')
|
20
|
+
end
|
21
|
+
|
22
|
+
if local?
|
23
|
+
to = validate_to || stream.user.jid.bare
|
24
|
+
recipients = stream.connected_resources(to)
|
25
|
+
if recipients.empty?
|
26
|
+
if user = storage(to.domain).find_user(to)
|
27
|
+
msg_body = self.css('body').inner_text
|
28
|
+
unless msg_body==""
|
29
|
+
msg = {:from => stream.user.jid.to_s,:body=>msg_body,:to => to.bare.to_s}
|
30
|
+
storage(to.domain).save_offline_message(msg)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
else
|
34
|
+
broadcast(recipients)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
self[FROM] = stream.user.jid.to_s
|
38
|
+
route
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Presence < Stanza
|
6
|
+
register "/presence"
|
7
|
+
|
8
|
+
VALID_TYPES = %w[subscribe subscribed unsubscribe unsubscribed unavailable probe error].freeze
|
9
|
+
|
10
|
+
VALID_TYPES.each do |type|
|
11
|
+
define_method "#{type}?" do
|
12
|
+
self['type'] == type
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def process
|
17
|
+
stream.last_broadcast_presence = @node.clone unless validate_to
|
18
|
+
unless self['type'].nil?
|
19
|
+
raise StanzaErrors::BadRequest.new(self, 'modify')
|
20
|
+
end
|
21
|
+
dir = outbound? ? 'outbound' : 'inbound'
|
22
|
+
method("#{dir}_broadcast_presence").call
|
23
|
+
end
|
24
|
+
|
25
|
+
def outbound?
|
26
|
+
!inbound?
|
27
|
+
end
|
28
|
+
|
29
|
+
def inbound?
|
30
|
+
stream.class == Vines::Stream::Server ||
|
31
|
+
stream.class == Vines::Stream::Component
|
32
|
+
end
|
33
|
+
|
34
|
+
def outbound_broadcast_presence
|
35
|
+
self['from'] = stream.user.jid.to_s
|
36
|
+
to = validate_to
|
37
|
+
type = (self['type'] || '').strip
|
38
|
+
initial = to.nil? && type.empty? && !stream.available?
|
39
|
+
|
40
|
+
recipients = if to.nil?
|
41
|
+
stream.available_subscribers
|
42
|
+
else
|
43
|
+
stream.user.subscribed_from?(to) ? stream.available_resources(to) : []
|
44
|
+
end
|
45
|
+
|
46
|
+
broadcast(recipients)
|
47
|
+
broadcast(stream.available_resources(stream.user.jid))
|
48
|
+
|
49
|
+
if initial
|
50
|
+
stream.available_subscribed_to_resources.each do |recipient|
|
51
|
+
if recipient.last_broadcast_presence
|
52
|
+
el = recipient.last_broadcast_presence.clone
|
53
|
+
el['to'] = stream.user.jid.to_s
|
54
|
+
el['from'] = recipient.user.jid.to_s
|
55
|
+
stream.write(el)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
stream.remote_subscribed_to_contacts.each do |contact|
|
59
|
+
send_probe(contact.jid.bare)
|
60
|
+
end
|
61
|
+
#offline messages
|
62
|
+
storage.fetch_offline_messages(stream.user.jid).each do |offline_msg|
|
63
|
+
doc = Document.new
|
64
|
+
node = doc.create_element('message') do |el|
|
65
|
+
el['from'] = offline_msg[:from]
|
66
|
+
el['to'] = stream.user.jid.to_s
|
67
|
+
el['type'] = "chat"
|
68
|
+
el << doc.create_element("body") do |b|
|
69
|
+
b << offline_msg[:body]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
stream.write(node)
|
73
|
+
end
|
74
|
+
storage.delete_offline_messages(stream.user.jid)
|
75
|
+
stream.available!
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
stream.remote_subscribers(to).each do |contact|
|
80
|
+
node = @node.clone
|
81
|
+
node['to'] = contact.jid.bare.to_s
|
82
|
+
router.route(node) rescue nil # ignore RemoteServerNotFound
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def inbound_broadcast_presence
|
87
|
+
broadcast(stream.available_resources(validate_to))
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def send_probe(to)
|
93
|
+
to = JID.new(to)
|
94
|
+
doc = Document.new
|
95
|
+
probe = doc.create_element('presence',
|
96
|
+
'from' => stream.user.jid.bare.to_s,
|
97
|
+
'id' => Kit.uuid,
|
98
|
+
'to' => to.bare.to_s,
|
99
|
+
'type' => 'probe')
|
100
|
+
router.route(probe) rescue nil # ignore RemoteServerNotFound
|
101
|
+
end
|
102
|
+
|
103
|
+
def auto_reply_to_subscription_request(from, type)
|
104
|
+
doc = Document.new
|
105
|
+
node = doc.create_element('presence') do |el|
|
106
|
+
el['from'] = from.to_s
|
107
|
+
el['id'] = self['id'] if self['id']
|
108
|
+
el['to'] = stream.user.jid.bare.to_s
|
109
|
+
el['type'] = type
|
110
|
+
end
|
111
|
+
stream.write(node)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Send the contact's roster item to the current user's interested streams.
|
115
|
+
# Roster pushes are required, following presence subscription updates, to
|
116
|
+
# notify the user's clients of the contact's current state.
|
117
|
+
def send_roster_push(to)
|
118
|
+
contact = stream.user.contact(to)
|
119
|
+
stream.interested_resources(stream.user.jid).each do |recipient|
|
120
|
+
contact.send_roster_push(recipient)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Notify the current user's interested streams of a contact's subscription
|
125
|
+
# state change as a result of receiving a subscribed, unsubscribe, or
|
126
|
+
# unsubscribed presence stanza.
|
127
|
+
def broadcast_subscription_change(contact)
|
128
|
+
stamp_from
|
129
|
+
stream.interested_resources(stamp_to).each do |recipient|
|
130
|
+
@node['to'] = recipient.user.jid.to_s
|
131
|
+
recipient.write(@node)
|
132
|
+
contact.send_roster_push(recipient)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Validate that the incoming stanza has a 'to' attribute and strip any
|
137
|
+
# resource part from it so it's a bare jid. Return the bare JID object
|
138
|
+
# that was stamped.
|
139
|
+
def stamp_to
|
140
|
+
to = validate_to
|
141
|
+
raise StanzaErrors::BadRequest.new(self, 'modify') unless to
|
142
|
+
to.bare.tap do |bare|
|
143
|
+
self['to'] = bare.to_s
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Presence subscription stanzas must be addressed from the user's bare
|
148
|
+
# JID. Return the user's bare JID object that was stamped.
|
149
|
+
def stamp_from
|
150
|
+
stream.user.jid.bare.tap do |bare|
|
151
|
+
self['from'] = bare.to_s
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Presence
|
6
|
+
class Error < Presence
|
7
|
+
register "/presence[@type='error']"
|
8
|
+
|
9
|
+
def process
|
10
|
+
inbound? ? process_inbound : process_outbound
|
11
|
+
end
|
12
|
+
|
13
|
+
def process_outbound
|
14
|
+
# FIXME Implement error handling
|
15
|
+
end
|
16
|
+
|
17
|
+
def process_inbound
|
18
|
+
# FIXME Implement error handling
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Presence
|
6
|
+
class Probe < Presence
|
7
|
+
register "/presence[@type='probe']"
|
8
|
+
|
9
|
+
def process
|
10
|
+
inbound? ? process_inbound : process_outbound
|
11
|
+
end
|
12
|
+
|
13
|
+
def process_outbound
|
14
|
+
self['from'] = stream.user.jid.to_s
|
15
|
+
local? ? process_inbound : route
|
16
|
+
end
|
17
|
+
|
18
|
+
def process_inbound
|
19
|
+
to = validate_to
|
20
|
+
raise StanzaErrors::BadRequest.new(self, 'modify') unless to
|
21
|
+
|
22
|
+
user = storage(to.domain).find_user(to)
|
23
|
+
unless user && user.subscribed_from?(stream.user.jid)
|
24
|
+
auto_reply_to_subscription_request(to.bare, 'unsubscribed')
|
25
|
+
else
|
26
|
+
stream.available_resources(to).each do |recipient|
|
27
|
+
el = recipient.last_broadcast_presence.clone
|
28
|
+
el['from'] = recipient.user.jid.to_s
|
29
|
+
el['to'] = stream.user.jid.to_s
|
30
|
+
stream.write(el)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Presence
|
6
|
+
class Subscribe < Presence
|
7
|
+
register "/presence[@type='subscribe']"
|
8
|
+
|
9
|
+
def process
|
10
|
+
stamp_from
|
11
|
+
inbound? ? process_inbound : process_outbound
|
12
|
+
end
|
13
|
+
|
14
|
+
def process_outbound
|
15
|
+
to = stamp_to
|
16
|
+
stream.user.request_subscription(to)
|
17
|
+
storage.save_user(stream.user)
|
18
|
+
stream.update_user_streams(stream.user)
|
19
|
+
local? ? process_inbound : route
|
20
|
+
send_roster_push(to)
|
21
|
+
end
|
22
|
+
|
23
|
+
def process_inbound
|
24
|
+
to = stamp_to
|
25
|
+
contact = storage(to.domain).find_user(to)
|
26
|
+
if contact.nil?
|
27
|
+
auto_reply_to_subscription_request(to, 'unsubscribed')
|
28
|
+
elsif contact.subscribed_from?(stream.user.jid)
|
29
|
+
auto_reply_to_subscription_request(to, 'subscribed')
|
30
|
+
else
|
31
|
+
recipients = stream.available_resources(to)
|
32
|
+
if recipients.empty?
|
33
|
+
# TODO store subscription request per RFC 6121 3.1.3 #4
|
34
|
+
else
|
35
|
+
broadcast_to_available_resources([@node], to)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Presence
|
6
|
+
class Subscribed < Presence
|
7
|
+
register "/presence[@type='subscribed']"
|
8
|
+
|
9
|
+
def process
|
10
|
+
stamp_from
|
11
|
+
inbound? ? process_inbound : process_outbound
|
12
|
+
end
|
13
|
+
|
14
|
+
def process_outbound
|
15
|
+
to = stamp_to
|
16
|
+
stream.user.add_subscription_from(to)
|
17
|
+
storage.save_user(stream.user)
|
18
|
+
stream.update_user_streams(stream.user)
|
19
|
+
local? ? process_inbound : route
|
20
|
+
send_roster_push(to)
|
21
|
+
send_known_presence(to)
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_inbound
|
25
|
+
to = stamp_to
|
26
|
+
user = storage(to.domain).find_user(to)
|
27
|
+
contact = user.contact(stream.user.jid) if user
|
28
|
+
return unless contact && contact.can_subscribe?
|
29
|
+
contact.subscribe_to
|
30
|
+
storage(to.domain).save_user(user)
|
31
|
+
stream.update_user_streams(user)
|
32
|
+
broadcast_subscription_change(contact)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
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
|
45
|
+
end
|
46
|
+
broadcast_to_available_resources(stanzas, to)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|