vines 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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