vines 0.2.1 → 0.3.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.
Files changed (96) hide show
  1. data/README +1 -1
  2. data/Rakefile +10 -10
  3. data/conf/certs/ca-bundle.crt +112 -378
  4. data/conf/config.rb +18 -9
  5. data/lib/vines.rb +8 -1
  6. data/lib/vines/command/cert.rb +2 -1
  7. data/lib/vines/command/init.rb +11 -0
  8. data/lib/vines/command/ldap.rb +6 -3
  9. data/lib/vines/command/schema.rb +1 -1
  10. data/lib/vines/config.rb +57 -146
  11. data/lib/vines/config/host.rb +85 -0
  12. data/lib/vines/config/port.rb +111 -0
  13. data/lib/vines/contact.rb +1 -1
  14. data/lib/vines/jid.rb +26 -4
  15. data/lib/vines/kit.rb +6 -0
  16. data/lib/vines/log.rb +24 -0
  17. data/lib/vines/router.rb +70 -38
  18. data/lib/vines/stanza.rb +45 -8
  19. data/lib/vines/stanza/iq.rb +3 -3
  20. data/lib/vines/stanza/iq/disco_info.rb +5 -1
  21. data/lib/vines/stanza/iq/disco_items.rb +3 -0
  22. data/lib/vines/stanza/iq/private_storage.rb +9 -5
  23. data/lib/vines/stanza/iq/roster.rb +11 -12
  24. data/lib/vines/stanza/iq/vcard.rb +4 -4
  25. data/lib/vines/stanza/iq/version.rb +25 -0
  26. data/lib/vines/stanza/message.rb +4 -5
  27. data/lib/vines/stanza/presence.rb +20 -18
  28. data/lib/vines/stanza/presence/probe.rb +3 -4
  29. data/lib/vines/stanza/presence/subscribe.rb +4 -3
  30. data/lib/vines/stanza/presence/subscribed.rb +6 -5
  31. data/lib/vines/stanza/presence/unsubscribe.rb +4 -4
  32. data/lib/vines/stanza/presence/unsubscribed.rb +4 -3
  33. data/lib/vines/storage/couchdb.rb +3 -3
  34. data/lib/vines/storage/ldap.rb +19 -8
  35. data/lib/vines/storage/local.rb +23 -12
  36. data/lib/vines/storage/redis.rb +3 -3
  37. data/lib/vines/storage/sql.rb +5 -5
  38. data/lib/vines/stream.rb +40 -6
  39. data/lib/vines/stream/client.rb +5 -6
  40. data/lib/vines/stream/client/auth.rb +3 -2
  41. data/lib/vines/stream/client/bind.rb +2 -2
  42. data/lib/vines/stream/client/bind_restart.rb +1 -2
  43. data/lib/vines/stream/client/ready.rb +2 -0
  44. data/lib/vines/stream/client/session.rb +13 -4
  45. data/lib/vines/stream/client/tls.rb +1 -0
  46. data/lib/vines/stream/component.rb +6 -5
  47. data/lib/vines/stream/component/ready.rb +5 -6
  48. data/lib/vines/stream/http.rb +10 -4
  49. data/lib/vines/stream/http/request.rb +23 -2
  50. data/lib/vines/stream/server.rb +13 -11
  51. data/lib/vines/stream/server/outbound/auth_result.rb +1 -0
  52. data/lib/vines/stream/server/outbound/tls_result.rb +1 -0
  53. data/lib/vines/stream/server/ready.rb +2 -2
  54. data/lib/vines/user.rb +2 -1
  55. data/lib/vines/version.rb +1 -1
  56. data/test/config/host_test.rb +292 -0
  57. data/test/config_test.rb +244 -103
  58. data/test/contact_test.rb +7 -1
  59. data/test/jid_test.rb +48 -0
  60. data/test/router_test.rb +16 -47
  61. data/test/stanza/iq/disco_info_test.rb +76 -0
  62. data/test/stanza/iq/disco_items_test.rb +47 -0
  63. data/test/stanza/iq/private_storage_test.rb +33 -10
  64. data/test/stanza/iq/roster_test.rb +15 -5
  65. data/test/stanza/iq/vcard_test.rb +8 -25
  66. data/test/stanza/iq/version_test.rb +62 -0
  67. data/test/stanza/iq_test.rb +13 -10
  68. data/test/stanza/message_test.rb +16 -24
  69. data/test/stanza/presence/probe_test.rb +52 -0
  70. data/test/stanza/presence/subscribe_test.rb +1 -5
  71. data/test/stanza_test.rb +77 -0
  72. data/test/stream/client/auth_test.rb +1 -0
  73. data/test/stream/client/ready_test.rb +2 -0
  74. data/test/stream/client/session_test.rb +7 -2
  75. data/test/stream/component/ready_test.rb +19 -36
  76. data/test/stream/http/request_test.rb +22 -2
  77. data/test/stream/server/ready_test.rb +14 -21
  78. data/web/404.html +9 -3
  79. data/web/chat/index.html +2 -2
  80. data/web/chat/javascripts/app.js +1 -1
  81. data/web/chat/stylesheets/chat.css +4 -9
  82. data/web/lib/coffeescripts/layout.coffee +2 -2
  83. data/web/{chat → lib}/coffeescripts/logout.coffee +0 -0
  84. data/web/lib/coffeescripts/notification.coffee +14 -0
  85. data/web/lib/coffeescripts/session.coffee +28 -24
  86. data/web/lib/coffeescripts/transfer.coffee +37 -34
  87. data/web/lib/javascripts/base.js +8 -8
  88. data/web/lib/javascripts/icons.js +3 -0
  89. data/web/lib/javascripts/jquery.js +4 -18
  90. data/web/lib/javascripts/layout.js +2 -2
  91. data/web/{chat → lib}/javascripts/logout.js +0 -0
  92. data/web/lib/javascripts/notification.js +26 -0
  93. data/web/lib/javascripts/session.js +20 -16
  94. data/web/lib/javascripts/transfer.js +45 -55
  95. data/web/lib/stylesheets/base.css +45 -9
  96. metadata +31 -15
@@ -33,11 +33,11 @@ module Vines
33
33
  private
34
34
 
35
35
  def route_iq
36
- to = (self['to'] || '').strip
37
- return false if to.empty? || to == stream.domain
36
+ to = validate_to
37
+ return false if to.nil? || to.to_s == stream.domain
38
38
  self['from'] = stream.user.jid.to_s
39
39
  if local?
40
- router.available_resources(to).each do |recipient|
40
+ stream.available_resources(to).each do |recipient|
41
41
  recipient.write(@node)
42
42
  end
43
43
  else
@@ -13,9 +13,13 @@ module Vines
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
+ query << el.document.create_element('identity', 'category' => 'server', 'type' => 'im')
17
+ query << el.document.create_element('feature', 'var' => NAMESPACES[:disco_info])
18
+ query << el.document.create_element('feature', 'var' => NAMESPACES[:disco_items])
16
19
  query << el.document.create_element('feature', 'var' => NAMESPACES[:ping])
17
20
  query << el.document.create_element('feature', 'var' => NAMESPACES[:vcard])
18
- if stream.private_storage?
21
+ query << el.document.create_element('feature', 'var' => NAMESPACES[:version])
22
+ if stream.config.private_storage?(stream.domain)
19
23
  query << el.document.create_element('feature', 'var' => NAMESPACES[:storage])
20
24
  end
21
25
  end
@@ -13,6 +13,9 @@ module Vines
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
+ stream.config.vhosts[stream.domain].components.keys.sort.each do |domain|
17
+ query << el.document.create_element('item', 'jid' => domain)
18
+ end
16
19
  end
17
20
  end
18
21
  stream.write(result)
@@ -12,10 +12,8 @@ module Vines
12
12
  register "/iq[@id and (@type='get' or @type='set')]/ns:query", 'ns' => NS
13
13
 
14
14
  def process
15
- unless stream.private_storage?
16
- raise StanzaErrors::ServiceUnavailable.new(self, 'cancel')
17
- end
18
15
  validate_to_address
16
+ validate_storage_enabled
19
17
  validate_children_size
20
18
  validate_namespaces
21
19
  get? ? retrieve_fragment : update_fragment
@@ -64,12 +62,18 @@ module Vines
64
62
  end
65
63
 
66
64
  def validate_to_address
67
- to = (self['to'] || '').strip
68
- unless to.empty? || to == stream.user.jid.bare.to_s
65
+ to = validate_to
66
+ unless to.nil? || to == stream.user.jid.bare
69
67
  raise StanzaErrors::Forbidden.new(self, 'cancel')
70
68
  end
71
69
  end
72
70
 
71
+ def validate_storage_enabled
72
+ unless stream.config.private_storage?(stream.domain)
73
+ raise StanzaErrors::ServiceUnavailable.new(self, 'cancel')
74
+ end
75
+ end
76
+
73
77
  def validate_namespaces
74
78
  elements.first.elements.each do |node|
75
79
  if node.namespace.nil? || NAMESPACES.values.include?(node.namespace.href)
@@ -9,6 +9,7 @@ module Vines
9
9
  register "/iq[@id and (@type='get' or @type='set')]/ns:query", 'ns' => NS
10
10
 
11
11
  def process
12
+ validate_to_address
12
13
  get? ? roster_query : update_roster
13
14
  end
14
15
 
@@ -26,8 +27,8 @@ module Vines
26
27
  # Roster sets must have no 'to' address or be addressed to the same
27
28
  # JID that sent the stanza. RFC 6121 sections 2.1.5 and 2.3.3.
28
29
  def validate_to_address
29
- to = (self['to'] || '').strip
30
- unless to.empty? || JID.new(to).bare == stream.user.jid.bare
30
+ to = validate_to
31
+ unless to.nil? || to.bare == stream.user.jid.bare
31
32
  raise StanzaErrors::Forbidden.new(self, 'auth')
32
33
  end
33
34
  end
@@ -35,14 +36,12 @@ module Vines
35
36
  # Add, update, or delete the roster item contained in the iq set
36
37
  # stanza received from the client. RFC 6121 sections 2.3, 2.4, 2.5.
37
38
  def update_roster
38
- validate_to_address
39
-
40
39
  items = self.xpath('ns:query/ns:item', 'ns' => NS)
41
40
  raise StanzaErrors::BadRequest.new(self, 'modify') if items.size != 1
42
41
  item = items.first
43
42
 
44
- jid = (item['jid'] || '').strip.empty? ? nil : JID.new(item['jid'].strip)
45
- raise StanzaErrors::BadRequest.new(self, 'modify') unless jid && jid.bare?
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?
46
45
 
47
46
  if item['subscription'] == 'remove'
48
47
  remove_contact(jid)
@@ -75,7 +74,7 @@ module Vines
75
74
  def remove_contact(jid)
76
75
  contact = stream.user.contact(jid)
77
76
  raise StanzaErrors::ItemNotFound.new(self, 'modify') unless contact
78
- if router.local_jid?(contact.jid)
77
+ if local_jid?(contact.jid)
79
78
  user = storage(contact.jid.domain).find_user(contact.jid)
80
79
  end
81
80
 
@@ -94,10 +93,10 @@ module Vines
94
93
  :jid => contact.jid,
95
94
  :subscription => 'remove'))
96
95
 
97
- if router.local_jid?(contact.jid)
96
+ if local_jid?(contact.jid)
98
97
  send_unavailable(stream.user.jid, contact.jid) if contact.subscribed_from?
99
98
  send_unsubscribe(contact)
100
- if user.contact(stream.user.jid)
99
+ if user && user.contact(stream.user.jid)
101
100
  push_roster_updates(contact.jid, user.contact(stream.user.jid))
102
101
  end
103
102
  else
@@ -112,8 +111,8 @@ module Vines
112
111
  presence(contact.jid, type) if contact.send("subscribed_#{meth}?")
113
112
  end.compact
114
113
 
115
- if router.local_jid?(contact.jid)
116
- router.interested_resources(contact.jid).each do |recipient|
114
+ if local_jid?(contact.jid)
115
+ stream.interested_resources(contact.jid).each do |recipient|
117
116
  presence.each {|el| recipient.write(el) }
118
117
  end
119
118
  else
@@ -133,7 +132,7 @@ module Vines
133
132
  # Send an iq set stanza to the user's interested resources, letting them
134
133
  # know their roster has been updated.
135
134
  def push_roster_updates(to, contact)
136
- router.interested_resources(to).each do |recipient|
135
+ stream.interested_resources(to).each do |recipient|
137
136
  contact.send_roster_push(recipient)
138
137
  end
139
138
  end
@@ -20,8 +20,8 @@ module Vines
20
20
  private
21
21
 
22
22
  def vcard_query
23
- jid = (self['to'] || '').strip
24
- jid = jid.empty? ? stream.user.jid.bare : JID.new(jid).bare
23
+ to = validate_to
24
+ jid = to ? to.bare : stream.user.jid.bare
25
25
  card = storage.find_vcard(jid)
26
26
 
27
27
  raise StanzaErrors::ItemNotFound.new(self, 'cancel') unless card
@@ -38,8 +38,8 @@ module Vines
38
38
  end
39
39
 
40
40
  def vcard_update
41
- to = (self['to'] || '').strip
42
- unless to.empty? || to == stream.user.jid.bare.to_s
41
+ to = validate_to
42
+ unless to.nil? || to == stream.user.jid.bare
43
43
  raise StanzaErrors::Forbidden.new(self, 'auth')
44
44
  end
45
45
 
@@ -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
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
@@ -5,8 +5,8 @@ module Vines
5
5
  class Message < Stanza
6
6
  register "/message"
7
7
 
8
- TYPE, TO, FROM = %w[type to from].map {|s| s.freeze }
9
- VALID_TYPES = %w[chat error groupchat headline normal].freeze
8
+ TYPE, FROM = %w[type from].map {|s| s.freeze }
9
+ VALID_TYPES = %w[chat error groupchat headline normal].freeze
10
10
 
11
11
  VALID_TYPES.each do |type|
12
12
  define_method "#{type}?" do
@@ -20,9 +20,8 @@ module Vines
20
20
  end
21
21
 
22
22
  if local?
23
- to = (self[TO] || '').strip
24
- to = to.empty? ? stream.user.jid.bare : JID.new(to)
25
- recipients = router.connected_resources(to)
23
+ to = validate_to || stream.user.jid.bare
24
+ recipients = stream.connected_resources(to)
26
25
  if recipients.empty?
27
26
  if user = storage(to.domain).find_user(to)
28
27
  # TODO Implement offline messaging storage
@@ -14,7 +14,7 @@ module Vines
14
14
  end
15
15
 
16
16
  def process
17
- stream.last_broadcast_presence = @node.clone unless self['to']
17
+ stream.last_broadcast_presence = @node.clone unless validate_to
18
18
  unless self['type'].nil?
19
19
  raise StanzaErrors::BadRequest.new(self, 'modify')
20
20
  end
@@ -23,26 +23,28 @@ module Vines
23
23
  end
24
24
 
25
25
  def outbound?
26
- stream.class != Vines::Stream::Server
26
+ !inbound?
27
27
  end
28
28
 
29
29
  def inbound?
30
- stream.class == Vines::Stream::Server
30
+ stream.class == Vines::Stream::Server ||
31
+ stream.class == Vines::Stream::Component
31
32
  end
32
33
 
33
34
  def outbound_broadcast_presence
34
35
  self['from'] = stream.user.jid.to_s
35
- to, type = %w[to type].map {|a| (self[a] || '').strip }
36
- initial = to.empty? && type.empty? && !stream.available?
36
+ to = validate_to
37
+ type = (self['type'] || '').strip
38
+ initial = to.nil? && type.empty? && !stream.available?
37
39
 
38
- recipients = if to.empty?
40
+ recipients = if to.nil?
39
41
  stream.available_subscribers
40
42
  else
41
- stream.user.subscribed_from?(to) ? router.available_resources(to) : []
43
+ stream.user.subscribed_from?(to) ? stream.available_resources(to) : []
42
44
  end
43
45
 
44
46
  broadcast(recipients)
45
- broadcast(router.available_resources(stream.user.jid))
47
+ broadcast(stream.available_resources(stream.user.jid))
46
48
 
47
49
  if initial
48
50
  stream.available_subscribed_to_resources.each do |recipient|
@@ -53,6 +55,9 @@ module Vines
53
55
  stream.write(el)
54
56
  end
55
57
  end
58
+ stream.remote_subscribed_to_contacts.each do |contact|
59
+ send_probe(contact.jid.bare)
60
+ end
56
61
  stream.available!
57
62
  end
58
63
 
@@ -60,12 +65,11 @@ module Vines
60
65
  node = @node.clone
61
66
  node['to'] = contact.jid.bare.to_s
62
67
  router.route(node) rescue nil # ignore RemoteServerNotFound
63
- send_probe(contact.jid.bare) if initial
64
68
  end
65
69
  end
66
70
 
67
71
  def inbound_broadcast_presence
68
- broadcast(router.available_resources(self['to']))
72
+ broadcast(stream.available_resources(validate_to))
69
73
  end
70
74
 
71
75
  private
@@ -78,7 +82,7 @@ module Vines
78
82
  'id' => Kit.uuid,
79
83
  'to' => to.bare.to_s,
80
84
  'type' => 'probe')
81
- router.route(probe)
85
+ router.route(probe) rescue nil # ignore RemoteServerNotFound
82
86
  end
83
87
 
84
88
  def auto_reply_to_subscription_request(from, type)
@@ -96,13 +100,11 @@ module Vines
96
100
  # resource part from it so it's a bare jid. Return the bare JID object
97
101
  # that was stamped.
98
102
  def stamp_to
99
- to = (self['to'] || '').strip
100
- raise StanzaErrors::BadRequest.new(self, 'modify') if to.empty?
101
- to = JID.new(to).bare
102
- malformed = [to.node, to.domain].any? {|part| (part || '').strip.empty? }
103
- raise StanzaErrors::JidMalformed.new(self, 'modify') if malformed
104
- self['to'] = to.to_s
105
- to
103
+ to = validate_to
104
+ raise StanzaErrors::BadRequest.new(self, 'modify') unless to
105
+ to.bare.tap do |bare|
106
+ self['to'] = bare.to_s
107
+ end
106
108
  end
107
109
  end
108
110
  end
@@ -16,15 +16,14 @@ module Vines
16
16
  end
17
17
 
18
18
  def process_inbound
19
- to = (self['to'] || '').strip
20
- raise StanzaErrors::BadRequest.new(self, 'modify') if to.empty?
21
- to = JID.new(to)
19
+ to = validate_to
20
+ raise StanzaErrors::BadRequest.new(self, 'modify') unless to
22
21
 
23
22
  user = storage(to.domain).find_user(to)
24
23
  unless user && user.subscribed_from?(stream.user.jid)
25
24
  auto_reply_to_subscription_request(to.bare, 'unsubscribed')
26
25
  else
27
- router.available_resources(to).each do |recipient|
26
+ stream.available_resources(to).each do |recipient|
28
27
  el = recipient.last_broadcast_presence.clone
29
28
  el['from'] = recipient.user.jid.to_s
30
29
  el['to'] = stream.user.jid.to_s
@@ -13,14 +13,15 @@ module Vines
13
13
  def process_outbound
14
14
  self['from'] = stream.user.jid.bare.to_s
15
15
  to = stamp_to
16
- local? ? process_inbound : route
17
16
 
18
17
  stream.user.request_subscription(to)
19
18
  storage.save_user(stream.user)
20
19
  stream.update_user_streams(stream.user)
21
20
 
21
+ local? ? process_inbound : route
22
+
22
23
  contact = stream.user.contact(to)
23
- router.interested_resources(stream.user.jid).each do |recipient|
24
+ stream.interested_resources(stream.user.jid).each do |recipient|
24
25
  contact.send_roster_push(recipient)
25
26
  end
26
27
  end
@@ -35,7 +36,7 @@ module Vines
35
36
  elsif contact.subscribed_from?(stream.user.jid)
36
37
  auto_reply_to_subscription_request(to, 'subscribed')
37
38
  else
38
- recipients = router.available_resources(to)
39
+ recipients = stream.available_resources(to)
39
40
  if recipients.empty?
40
41
  # TODO store subscription request per RFC 6121 3.1.3 #4
41
42
  else
@@ -13,18 +13,19 @@ module Vines
13
13
  def process_outbound
14
14
  self['from'] = stream.user.jid.bare.to_s
15
15
  to = stamp_to
16
- local? ? process_inbound : route
17
16
 
18
17
  stream.user.add_subscription_from(to)
19
18
  storage.save_user(stream.user)
20
19
  stream.update_user_streams(stream.user)
21
20
 
21
+ local? ? process_inbound : route
22
+
22
23
  contact = stream.user.contact(to)
23
- router.interested_resources(stream.user.jid).each do |recipient|
24
+ stream.interested_resources(stream.user.jid).each do |recipient|
24
25
  contact.send_roster_push(recipient)
25
26
  end
26
27
 
27
- presences = router.available_resources(stream.user.jid).map do |c|
28
+ presences = stream.available_resources(stream.user.jid).map do |c|
28
29
  c.last_broadcast_presence.clone.tap do |node|
29
30
  node['from'] = c.user.jid.to_s
30
31
  node['id'] = Kit.uuid
@@ -33,7 +34,7 @@ module Vines
33
34
  end
34
35
 
35
36
  if local?
36
- router.available_resources(to).each do |recipient|
37
+ stream.available_resources(to).each do |recipient|
37
38
  presences.each {|el| recipient.write(el) }
38
39
  end
39
40
  else
@@ -52,7 +53,7 @@ module Vines
52
53
  storage(to.domain).save_user(user)
53
54
  stream.update_user_streams(user)
54
55
 
55
- router.interested_resources(to).each do |recipient|
56
+ stream.interested_resources(to).each do |recipient|
56
57
  recipient.write(@node)
57
58
  contact.send_roster_push(recipient)
58
59
  end
@@ -15,14 +15,14 @@ module Vines
15
15
  to = stamp_to
16
16
 
17
17
  return unless stream.user.subscribed_to?(to)
18
- local? ? process_inbound : route
19
-
20
18
  stream.user.remove_subscription_to(to)
21
19
  storage.save_user(stream.user)
22
20
  stream.update_user_streams(stream.user)
23
21
 
22
+ local? ? process_inbound : route
23
+
24
24
  contact = stream.user.contact(to)
25
- router.interested_resources(stream.user.jid).each do |recipient|
25
+ stream.interested_resources(stream.user.jid).each do |recipient|
26
26
  contact.send_roster_push(recipient)
27
27
  end
28
28
  end
@@ -38,7 +38,7 @@ module Vines
38
38
  storage(to.domain).save_user(user)
39
39
  stream.update_user_streams(user)
40
40
 
41
- router.interested_resources(to).each do |recipient|
41
+ stream.interested_resources(to).each do |recipient|
42
42
  recipient.write(@node)
43
43
  contact.send_roster_push(recipient)
44
44
  end
@@ -16,14 +16,15 @@ module Vines
16
16
 
17
17
  return unless stream.user.subscribed_from?(to)
18
18
  send_unavailable(stream.user.jid, to)
19
- local? ? process_inbound : route
20
19
 
21
20
  stream.user.remove_subscription_from(to)
22
21
  storage.save_user(stream.user)
23
22
  stream.update_user_streams(stream.user)
24
23
 
24
+ local? ? process_inbound : route
25
+
25
26
  contact = stream.user.contact(to)
26
- router.interested_resources(stream.user.jid).each do |recipient|
27
+ stream.interested_resources(stream.user.jid).each do |recipient|
27
28
  contact.send_roster_push(recipient)
28
29
  end
29
30
  end
@@ -39,7 +40,7 @@ module Vines
39
40
  storage(to.domain).save_user(user)
40
41
  stream.update_user_streams(user)
41
42
 
42
- router.interested_resources(to).each do |recipient|
43
+ stream.interested_resources(to).each do |recipient|
43
44
  recipient.write(@node)
44
45
  contact.send_roster_push(recipient)
45
46
  end