diaspora-vines 0.1.2
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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +7 -0
- data/Rakefile +23 -0
- data/bin/vines +4 -0
- data/conf/certs/README +39 -0
- data/conf/certs/ca-bundle.crt +3895 -0
- data/conf/config.rb +42 -0
- data/lib/vines/cli.rb +132 -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/cluster.rb +246 -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/ldap.rb +38 -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/config/host.rb +125 -0
- data/lib/vines/config/port.rb +132 -0
- data/lib/vines/config/pubsub.rb +108 -0
- data/lib/vines/config.rb +223 -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 +23 -0
- data/lib/vines/log.rb +24 -0
- data/lib/vines/router.rb +179 -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/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/iq.rb +48 -0
- data/lib/vines/stanza/message.rb +40 -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/presence.rb +141 -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 +175 -0
- data/lib/vines/storage/ldap.rb +71 -0
- data/lib/vines/storage/local.rb +139 -0
- data/lib/vines/storage/null.rb +39 -0
- data/lib/vines/storage/sql.rb +138 -0
- data/lib/vines/storage.rb +239 -0
- data/lib/vines/store.rb +110 -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/client.rb +84 -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/component.rb +58 -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/http.rb +157 -0
- data/lib/vines/stream/parser.rb +79 -0
- data/lib/vines/stream/sasl.rb +128 -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/server.rb +150 -0
- data/lib/vines/stream/state.rb +60 -0
- data/lib/vines/stream.rb +247 -0
- data/lib/vines/token_bucket.rb +55 -0
- data/lib/vines/user.rb +123 -0
- data/lib/vines/version.rb +6 -0
- data/lib/vines/xmpp_server.rb +25 -0
- data/lib/vines.rb +203 -0
- data/test/cluster/publisher_test.rb +57 -0
- data/test/cluster/sessions_test.rb +47 -0
- data/test/cluster/subscriber_test.rb +109 -0
- data/test/config/host_test.rb +369 -0
- data/test/config/pubsub_test.rb +187 -0
- data/test/config_test.rb +732 -0
- data/test/contact_test.rb +102 -0
- data/test/error_test.rb +58 -0
- data/test/ext/nokogiri.rb +14 -0
- data/test/jid_test.rb +147 -0
- data/test/kit_test.rb +31 -0
- data/test/router_test.rb +243 -0
- data/test/stanza/iq/disco_info_test.rb +78 -0
- data/test/stanza/iq/disco_items_test.rb +49 -0
- data/test/stanza/iq/private_storage_test.rb +184 -0
- data/test/stanza/iq/roster_test.rb +229 -0
- data/test/stanza/iq/session_test.rb +25 -0
- data/test/stanza/iq/vcard_test.rb +146 -0
- data/test/stanza/iq/version_test.rb +64 -0
- data/test/stanza/iq_test.rb +70 -0
- data/test/stanza/message_test.rb +126 -0
- data/test/stanza/presence/probe_test.rb +50 -0
- data/test/stanza/presence/subscribe_test.rb +83 -0
- data/test/stanza/pubsub/create_test.rb +116 -0
- data/test/stanza/pubsub/delete_test.rb +169 -0
- data/test/stanza/pubsub/publish_test.rb +309 -0
- data/test/stanza/pubsub/subscribe_test.rb +205 -0
- data/test/stanza/pubsub/unsubscribe_test.rb +148 -0
- data/test/stanza_test.rb +85 -0
- data/test/storage/ldap_test.rb +201 -0
- data/test/storage/local_test.rb +59 -0
- data/test/storage/mock_redis.rb +97 -0
- data/test/storage/null_test.rb +29 -0
- data/test/storage/storage_tests.rb +182 -0
- data/test/storage_test.rb +85 -0
- data/test/store_test.rb +130 -0
- data/test/stream/client/auth_test.rb +137 -0
- data/test/stream/client/ready_test.rb +47 -0
- data/test/stream/client/session_test.rb +27 -0
- data/test/stream/component/handshake_test.rb +52 -0
- data/test/stream/component/ready_test.rb +103 -0
- data/test/stream/component/start_test.rb +39 -0
- data/test/stream/http/auth_test.rb +70 -0
- data/test/stream/http/ready_test.rb +86 -0
- data/test/stream/http/request_test.rb +209 -0
- data/test/stream/http/sessions_test.rb +49 -0
- data/test/stream/http/start_test.rb +50 -0
- data/test/stream/parser_test.rb +122 -0
- data/test/stream/sasl_test.rb +195 -0
- data/test/stream/server/auth_test.rb +61 -0
- data/test/stream/server/outbound/auth_test.rb +75 -0
- data/test/stream/server/ready_test.rb +98 -0
- data/test/test_helper.rb +42 -0
- data/test/token_bucket_test.rb +44 -0
- data/test/user_test.rb +96 -0
- data/vines.gemspec +30 -0
- metadata +387 -0
@@ -0,0 +1,140 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Iq
|
6
|
+
class Roster < Query
|
7
|
+
NS = NAMESPACES[:roster]
|
8
|
+
|
9
|
+
register "/iq[@id and (@type='get' or @type='set')]/ns:query", 'ns' => NS
|
10
|
+
|
11
|
+
def process
|
12
|
+
validate_to_address
|
13
|
+
get? ? roster_query : update_roster
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Send an iq result stanza containing roster items to the user in
|
19
|
+
# response to their roster get request. Requesting the roster makes
|
20
|
+
# this stream an "interested resource" that can now receive roster
|
21
|
+
# updates.
|
22
|
+
def roster_query
|
23
|
+
stream.requested_roster!
|
24
|
+
stream.write(stream.user.to_roster_xml(self['id']))
|
25
|
+
end
|
26
|
+
|
27
|
+
# Roster sets must have no 'to' address or be addressed to the same
|
28
|
+
# JID that sent the stanza. RFC 6121 sections 2.1.5 and 2.3.3.
|
29
|
+
def validate_to_address
|
30
|
+
to = validate_to
|
31
|
+
unless to.nil? || to.bare == stream.user.jid.bare
|
32
|
+
raise StanzaErrors::Forbidden.new(self, 'auth')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Add, update, or delete the roster item contained in the iq set
|
37
|
+
# stanza received from the client. RFC 6121 sections 2.3, 2.4, 2.5.
|
38
|
+
def update_roster
|
39
|
+
items = self.xpath('ns:query/ns:item', 'ns' => NS)
|
40
|
+
raise StanzaErrors::BadRequest.new(self, 'modify') if items.size != 1
|
41
|
+
item = items.first
|
42
|
+
|
43
|
+
jid = JID.new(item['jid']) rescue (raise StanzaErrors::JidMalformed.new(self, 'modify'))
|
44
|
+
raise StanzaErrors::BadRequest.new(self, 'modify') if jid.empty? || !jid.bare?
|
45
|
+
|
46
|
+
if item['subscription'] == 'remove'
|
47
|
+
remove_contact(jid)
|
48
|
+
return
|
49
|
+
end
|
50
|
+
|
51
|
+
raise StanzaErrors::NotAllowed.new(self, 'modify') if jid == stream.user.jid.bare
|
52
|
+
groups = item.xpath('ns:group', 'ns' => NS).map {|g| g.text.strip }
|
53
|
+
raise StanzaErrors::BadRequest.new(self, 'modify') if groups.uniq!
|
54
|
+
raise StanzaErrors::NotAcceptable.new(self, 'modify') if groups.include?('')
|
55
|
+
|
56
|
+
contact = stream.user.contact(jid)
|
57
|
+
unless contact
|
58
|
+
contact = Contact.new(jid: jid)
|
59
|
+
stream.user.roster << contact
|
60
|
+
end
|
61
|
+
contact.name = item['name']
|
62
|
+
contact.groups = groups
|
63
|
+
storage.save_user(stream.user)
|
64
|
+
stream.update_user_streams(stream.user)
|
65
|
+
send_result_iq
|
66
|
+
push_roster_updates(stream.user.jid, contact)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Remove the contact with this JID from the user's roster and send
|
70
|
+
# roster pushes to the user's interested resources. This is triggered
|
71
|
+
# by receiving an iq set with an item element like
|
72
|
+
# <item jid="alice@wonderland.lit" subscription="remove"/>. RFC 6121
|
73
|
+
# section 2.5.
|
74
|
+
def remove_contact(jid)
|
75
|
+
contact = stream.user.contact(jid)
|
76
|
+
raise StanzaErrors::ItemNotFound.new(self, 'modify') unless contact
|
77
|
+
if local_jid?(contact.jid)
|
78
|
+
user = storage(contact.jid.domain).find_user(contact.jid)
|
79
|
+
end
|
80
|
+
|
81
|
+
if user && user.contact(stream.user.jid)
|
82
|
+
user.contact(stream.user.jid).subscription = 'none'
|
83
|
+
user.contact(stream.user.jid).ask = nil
|
84
|
+
end
|
85
|
+
stream.user.remove_contact(contact.jid)
|
86
|
+
[user, stream.user].compact.each do |save|
|
87
|
+
storage(save.jid.domain).save_user(save)
|
88
|
+
stream.update_user_streams(save)
|
89
|
+
end
|
90
|
+
|
91
|
+
send_result_iq
|
92
|
+
push_roster_updates(stream.user.jid,
|
93
|
+
Contact.new(jid: contact.jid, subscription: 'remove'))
|
94
|
+
|
95
|
+
if local_jid?(contact.jid)
|
96
|
+
send_unavailable(stream.user.jid, contact.jid) if contact.subscribed_from?
|
97
|
+
send_unsubscribe(contact)
|
98
|
+
if user && user.contact(stream.user.jid)
|
99
|
+
push_roster_updates(contact.jid, user.contact(stream.user.jid))
|
100
|
+
end
|
101
|
+
else
|
102
|
+
send_unsubscribe(contact)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Notify the contact that it's been removed from the user's roster
|
107
|
+
# and no longer has any presence relationship with the user.
|
108
|
+
def send_unsubscribe(contact)
|
109
|
+
presence = [%w[to unsubscribe], %w[from unsubscribed]].map do |meth, type|
|
110
|
+
presence(contact.jid, type) if contact.send("subscribed_#{meth}?")
|
111
|
+
end.compact
|
112
|
+
broadcast_to_interested_resources(presence, contact.jid)
|
113
|
+
end
|
114
|
+
|
115
|
+
def presence(to, type)
|
116
|
+
doc = Document.new
|
117
|
+
doc.create_element('presence',
|
118
|
+
'from' => stream.user.jid.bare.to_s,
|
119
|
+
'id' => Kit.uuid,
|
120
|
+
'to' => to.to_s,
|
121
|
+
'type' => type)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Send an iq set stanza to the user's interested resources, letting them
|
125
|
+
# know their roster has been updated.
|
126
|
+
def push_roster_updates(to, contact)
|
127
|
+
stream.interested_resources(to).each do |recipient|
|
128
|
+
contact.send_roster_push(recipient)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def send_result_iq
|
133
|
+
node = to_result
|
134
|
+
node.remove_attribute('from')
|
135
|
+
stream.write(node)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -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,48 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Iq < Stanza
|
6
|
+
register "/iq"
|
7
|
+
|
8
|
+
VALID_TYPES = %w[get set result 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
|
+
if self['id'] && VALID_TYPES.include?(self['type'])
|
18
|
+
route_iq or raise StanzaErrors::FeatureNotImplemented.new(@node, 'cancel')
|
19
|
+
else
|
20
|
+
raise StanzaErrors::BadRequest.new(@node, 'modify')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_result
|
25
|
+
doc = Document.new
|
26
|
+
doc.create_element('iq',
|
27
|
+
'from' => validate_to || stream.domain,
|
28
|
+
'id' => self['id'],
|
29
|
+
'to' => stream.user.jid,
|
30
|
+
'type' => 'result')
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
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.
|
39
|
+
def route_iq
|
40
|
+
to = validate_to
|
41
|
+
return false if to.nil? || stream.config.vhost?(to) || to_pubsub_domain?
|
42
|
+
self['from'] = stream.user.jid.to_s
|
43
|
+
local? ? broadcast(stream.connected_resources(to)) : route
|
44
|
+
true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,40 @@
|
|
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
|
+
# TODO Implement offline messaging storage
|
28
|
+
raise StanzaErrors::ServiceUnavailable.new(self, 'cancel')
|
29
|
+
end
|
30
|
+
else
|
31
|
+
broadcast(recipients)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
self[FROM] = stream.user.jid.to_s
|
35
|
+
route
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
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
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Presence
|
6
|
+
class Unavailable < Presence
|
7
|
+
register "/presence[@type='unavailable']"
|
8
|
+
|
9
|
+
def process
|
10
|
+
inbound? ? inbound_broadcast_presence : outbound_broadcast_presence
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Presence
|
6
|
+
class Unsubscribe < Presence
|
7
|
+
register "/presence[@type='unsubscribe']"
|
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
|
+
return unless stream.user.subscribed_to?(to)
|
17
|
+
stream.user.remove_subscription_to(to)
|
18
|
+
storage.save_user(stream.user)
|
19
|
+
stream.update_user_streams(stream.user)
|
20
|
+
local? ? process_inbound : route
|
21
|
+
send_roster_push(to)
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_inbound
|
25
|
+
to = stamp_to
|
26
|
+
user = storage(to.domain).find_user(to)
|
27
|
+
return unless user && user.subscribed_from?(stream.user.jid)
|
28
|
+
contact = user.contact(stream.user.jid)
|
29
|
+
contact.unsubscribe_from
|
30
|
+
storage(to.domain).save_user(user)
|
31
|
+
stream.update_user_streams(user)
|
32
|
+
broadcast_subscription_change(contact)
|
33
|
+
send_unavailable(to, stream.user.jid.bare)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Presence
|
6
|
+
class Unsubscribed < Presence
|
7
|
+
register "/presence[@type='unsubscribed']"
|
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
|
+
return unless stream.user.subscribed_from?(to)
|
17
|
+
send_unavailable(stream.user.jid, to)
|
18
|
+
stream.user.remove_subscription_from(to)
|
19
|
+
storage.save_user(stream.user)
|
20
|
+
stream.update_user_streams(stream.user)
|
21
|
+
local? ? process_inbound : route
|
22
|
+
send_roster_push(to)
|
23
|
+
end
|
24
|
+
|
25
|
+
def process_inbound
|
26
|
+
to = stamp_to
|
27
|
+
user = storage(to.domain).find_user(to)
|
28
|
+
return unless user && user.subscribed_to?(stream.user.jid)
|
29
|
+
contact = user.contact(stream.user.jid)
|
30
|
+
contact.unsubscribe_to
|
31
|
+
storage(to.domain).save_user(user)
|
32
|
+
stream.update_user_streams(user)
|
33
|
+
broadcast_subscription_change(contact)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|