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.
Files changed (115) hide show
  1. data/README +5 -9
  2. data/Rakefile +11 -9
  3. data/conf/config.rb +30 -4
  4. data/lib/vines/cluster/connection.rb +26 -0
  5. data/lib/vines/cluster/publisher.rb +55 -0
  6. data/lib/vines/cluster/pubsub.rb +92 -0
  7. data/lib/vines/cluster/sessions.rb +125 -0
  8. data/lib/vines/cluster/subscriber.rb +108 -0
  9. data/lib/vines/cluster.rb +246 -0
  10. data/lib/vines/command/init.rb +21 -24
  11. data/lib/vines/config/host.rb +48 -8
  12. data/lib/vines/config/port.rb +5 -0
  13. data/lib/vines/config/pubsub.rb +108 -0
  14. data/lib/vines/config.rb +74 -20
  15. data/lib/vines/jid.rb +14 -0
  16. data/lib/vines/router.rb +69 -55
  17. data/lib/vines/stanza/iq/disco_info.rb +22 -9
  18. data/lib/vines/stanza/iq/disco_items.rb +6 -3
  19. data/lib/vines/stanza/iq/ping.rb +1 -1
  20. data/lib/vines/stanza/iq/private_storage.rb +4 -8
  21. data/lib/vines/stanza/iq/roster.rb +6 -14
  22. data/lib/vines/stanza/iq/session.rb +2 -7
  23. data/lib/vines/stanza/iq/vcard.rb +4 -6
  24. data/lib/vines/stanza/iq/version.rb +1 -1
  25. data/lib/vines/stanza/iq.rb +8 -10
  26. data/lib/vines/stanza/presence/subscribe.rb +3 -11
  27. data/lib/vines/stanza/presence/subscribed.rb +16 -29
  28. data/lib/vines/stanza/presence/unsubscribe.rb +3 -15
  29. data/lib/vines/stanza/presence/unsubscribed.rb +3 -16
  30. data/lib/vines/stanza/presence.rb +30 -0
  31. data/lib/vines/stanza/pubsub/create.rb +39 -0
  32. data/lib/vines/stanza/pubsub/delete.rb +41 -0
  33. data/lib/vines/stanza/pubsub/publish.rb +66 -0
  34. data/lib/vines/stanza/pubsub/subscribe.rb +44 -0
  35. data/lib/vines/stanza/pubsub/unsubscribe.rb +30 -0
  36. data/lib/vines/stanza/pubsub.rb +22 -0
  37. data/lib/vines/stanza.rb +72 -22
  38. data/lib/vines/storage/couchdb.rb +46 -65
  39. data/lib/vines/storage/local.rb +20 -14
  40. data/lib/vines/storage/mongodb.rb +132 -0
  41. data/lib/vines/storage/null.rb +39 -0
  42. data/lib/vines/storage/redis.rb +61 -68
  43. data/lib/vines/storage/sql.rb +73 -69
  44. data/lib/vines/storage.rb +1 -1
  45. data/lib/vines/stream/client/bind.rb +2 -2
  46. data/lib/vines/stream/client/session.rb +71 -16
  47. data/lib/vines/stream/component/handshake.rb +1 -0
  48. data/lib/vines/stream/component/ready.rb +2 -2
  49. data/lib/vines/stream/http/session.rb +2 -0
  50. data/lib/vines/stream/http.rb +0 -6
  51. data/lib/vines/stream/server/final_restart.rb +1 -0
  52. data/lib/vines/stream/server/outbound/final_features.rb +1 -0
  53. data/lib/vines/stream/server/ready.rb +6 -2
  54. data/lib/vines/stream/server.rb +4 -3
  55. data/lib/vines/stream.rb +10 -6
  56. data/lib/vines/version.rb +1 -1
  57. data/lib/vines.rb +48 -22
  58. data/test/cluster/publisher_test.rb +45 -0
  59. data/test/cluster/sessions_test.rb +54 -0
  60. data/test/cluster/subscriber_test.rb +94 -0
  61. data/test/config/host_test.rb +100 -21
  62. data/test/config/pubsub_test.rb +181 -0
  63. data/test/config_test.rb +225 -43
  64. data/test/jid_test.rb +7 -0
  65. data/test/router_test.rb +181 -9
  66. data/test/stanza/iq/disco_info_test.rb +8 -6
  67. data/test/stanza/iq/disco_items_test.rb +3 -3
  68. data/test/stanza/iq/private_storage_test.rb +8 -19
  69. data/test/stanza/iq/roster_test.rb +1 -1
  70. data/test/stanza/iq/session_test.rb +3 -6
  71. data/test/stanza/iq/vcard_test.rb +6 -2
  72. data/test/stanza/iq/version_test.rb +3 -2
  73. data/test/stanza/iq_test.rb +5 -5
  74. data/test/stanza/message_test.rb +3 -2
  75. data/test/stanza/presence/probe_test.rb +2 -1
  76. data/test/stanza/pubsub/create_test.rb +138 -0
  77. data/test/stanza/pubsub/delete_test.rb +142 -0
  78. data/test/stanza/pubsub/publish_test.rb +373 -0
  79. data/test/stanza/pubsub/subscribe_test.rb +186 -0
  80. data/test/stanza/pubsub/unsubscribe_test.rb +179 -0
  81. data/test/stanza_test.rb +2 -1
  82. data/test/storage/local_test.rb +26 -25
  83. data/test/storage/mock_mongo.rb +40 -0
  84. data/test/storage/mock_redis.rb +98 -0
  85. data/test/storage/mongodb_test.rb +81 -0
  86. data/test/storage/null_test.rb +30 -0
  87. data/test/storage/redis_test.rb +3 -36
  88. data/test/stream/component/handshake_test.rb +4 -0
  89. data/test/stream/component/ready_test.rb +2 -1
  90. data/test/stream/server/ready_test.rb +7 -1
  91. data/web/404.html +5 -3
  92. data/web/chat/coffeescripts/chat.coffee +9 -5
  93. data/web/chat/javascripts/app.js +1 -1
  94. data/web/chat/javascripts/chat.js +14 -8
  95. data/web/chat/stylesheets/chat.css +4 -1
  96. data/web/lib/coffeescripts/button.coffee +9 -5
  97. data/web/lib/coffeescripts/filter.coffee +1 -1
  98. data/web/lib/coffeescripts/login.coffee +14 -1
  99. data/web/lib/coffeescripts/session.coffee +8 -11
  100. data/web/lib/images/dark-gray.png +0 -0
  101. data/web/lib/images/light-gray.png +0 -0
  102. data/web/lib/images/logo-large.png +0 -0
  103. data/web/lib/images/logo-small.png +0 -0
  104. data/web/lib/images/white.png +0 -0
  105. data/web/lib/javascripts/base.js +9 -8
  106. data/web/lib/javascripts/button.js +20 -12
  107. data/web/lib/javascripts/filter.js +1 -1
  108. data/web/lib/javascripts/icons.js +7 -1
  109. data/web/lib/javascripts/jquery.js +4 -4
  110. data/web/lib/javascripts/login.js +16 -2
  111. data/web/lib/javascripts/raphael.js +5 -7
  112. data/web/lib/javascripts/session.js +10 -14
  113. data/web/lib/stylesheets/base.css +7 -11
  114. data/web/lib/stylesheets/login.css +31 -27
  115. metadata +100 -34
@@ -13,8 +13,24 @@ module Vines
13
13
  end
14
14
  class Group < ActiveRecord::Base; end
15
15
  class User < ActiveRecord::Base
16
- has_many :contacts
17
- has_many :fragments
16
+ has_many :contacts, :dependent => :destroy
17
+ has_many :fragments, :dependent => :delete_all
18
+ end
19
+
20
+ # Wrap the method with ActiveRecord connection pool logic, so we properly
21
+ # return connections to the pool when we're finished with them. This also
22
+ # defers the original method by pushing it onto the EM thread pool because
23
+ # ActiveRecord uses blocking IO.
24
+ def self.with_connection(method, args={})
25
+ deferrable = args.key?(:defer) ? args[:defer] : true
26
+ old = "_with_connection_#{method}"
27
+ alias_method old, method
28
+ define_method method do |*args|
29
+ ActiveRecord::Base.connection_pool.with_connection do
30
+ method(old).call(*args)
31
+ end
32
+ end
33
+ defer(method) if deferrable
18
34
  end
19
35
 
20
36
  %w[adapter host port database username password pool].each do |name|
@@ -38,30 +54,26 @@ module Vines
38
54
  end
39
55
 
40
56
  def find_user(jid)
41
- ActiveRecord::Base.clear_reloadable_connections!
42
-
43
57
  jid = JID.new(jid).bare.to_s
44
58
  return if jid.empty?
45
59
  xuser = user_by_jid(jid)
46
- return Vines::User.new(:jid => jid).tap do |user|
60
+ return Vines::User.new(jid: jid).tap do |user|
47
61
  user.name, user.password = xuser.name, xuser.password
48
62
  xuser.contacts.each do |contact|
49
63
  groups = contact.groups.map {|group| group.name }
50
64
  user.roster << Vines::Contact.new(
51
- :jid => contact.jid,
52
- :name => contact.name,
53
- :subscription => contact.subscription,
54
- :ask => contact.ask,
55
- :groups => groups)
65
+ jid: contact.jid,
66
+ name: contact.name,
67
+ subscription: contact.subscription,
68
+ ask: contact.ask,
69
+ groups: groups)
56
70
  end
57
71
  end if xuser
58
72
  end
59
- defer :find_user
73
+ with_connection :find_user
60
74
 
61
75
  def save_user(user)
62
- ActiveRecord::Base.clear_reloadable_connections!
63
-
64
- xuser = user_by_jid(user.jid) || Sql::User.new(:jid => user.jid.bare.to_s)
76
+ xuser = user_by_jid(user.jid) || Sql::User.new(jid: user.jid.bare.to_s)
65
77
  xuser.name = user.name
66
78
  xuser.password = user.password
67
79
 
@@ -74,10 +86,10 @@ module Vines
74
86
  xuser.contacts.each do |contact|
75
87
  fresh = user.contact(contact.jid)
76
88
  contact.update_attributes(
77
- :name => fresh.name,
78
- :ask => fresh.ask,
79
- :subscription => fresh.subscription,
80
- :groups => groups(fresh))
89
+ name: fresh.name,
90
+ ask: fresh.ask,
91
+ subscription: fresh.subscription,
92
+ groups: groups(fresh))
81
93
  end
82
94
 
83
95
  # add new contacts to roster
@@ -85,112 +97,104 @@ module Vines
85
97
  user.roster.select {|contact| !jids.include?(contact.jid.bare.to_s) }
86
98
  .each do |contact|
87
99
  xuser.contacts.build(
88
- :user => xuser,
89
- :jid => contact.jid.bare.to_s,
90
- :name => contact.name,
91
- :ask => contact.ask,
92
- :subscription => contact.subscription,
93
- :groups => groups(contact))
100
+ user: xuser,
101
+ jid: contact.jid.bare.to_s,
102
+ name: contact.name,
103
+ ask: contact.ask,
104
+ subscription: contact.subscription,
105
+ groups: groups(contact))
94
106
  end
95
107
  xuser.save
96
108
  end
97
- defer :save_user
109
+ with_connection :save_user
98
110
 
99
111
  def find_vcard(jid)
100
- ActiveRecord::Base.clear_reloadable_connections!
101
-
102
112
  jid = JID.new(jid).bare.to_s
103
113
  return if jid.empty?
104
114
  if xuser = user_by_jid(jid)
105
115
  Nokogiri::XML(xuser.vcard).root rescue nil
106
116
  end
107
117
  end
108
- defer :find_vcard
118
+ with_connection :find_vcard
109
119
 
110
120
  def save_vcard(jid, card)
111
- ActiveRecord::Base.clear_reloadable_connections!
112
-
113
121
  xuser = user_by_jid(jid)
114
122
  if xuser
115
123
  xuser.vcard = card.to_xml
116
124
  xuser.save
117
125
  end
118
126
  end
119
- defer :save_vcard
127
+ with_connection :save_vcard
120
128
 
121
129
  def find_fragment(jid, node)
122
- ActiveRecord::Base.clear_reloadable_connections!
123
-
124
130
  jid = JID.new(jid).bare.to_s
125
131
  return if jid.empty?
126
132
  if fragment = fragment_by_jid(jid, node)
127
133
  Nokogiri::XML(fragment.xml).root rescue nil
128
134
  end
129
135
  end
130
- defer :find_fragment
136
+ with_connection :find_fragment
131
137
 
132
138
  def save_fragment(jid, node)
133
- ActiveRecord::Base.clear_reloadable_connections!
134
-
135
139
  jid = JID.new(jid).bare.to_s
136
140
  fragment = fragment_by_jid(jid, node) ||
137
141
  Sql::Fragment.new(
138
- :user => user_by_jid(jid),
139
- :root => node.name,
140
- :namespace => node.namespace.href)
142
+ user: user_by_jid(jid),
143
+ root: node.name,
144
+ namespace: node.namespace.href)
141
145
  fragment.xml = node.to_xml
142
146
  fragment.save
143
147
  end
144
- defer :save_fragment
148
+ with_connection :save_fragment
145
149
 
146
150
  # Create the tables and indexes used by this storage engine.
147
151
  def create_schema(args={})
148
- ActiveRecord::Base.clear_reloadable_connections!
149
-
150
152
  args[:force] ||= false
151
153
 
152
154
  ActiveRecord::Schema.define do
153
- create_table :users, :force => args[:force] do |t|
154
- t.string :jid, :limit => 2048, :null => false
155
- t.string :name, :limit => 1000, :null => true
156
- t.string :password, :limit => 1000, :null => true
157
- t.text :vcard, :null => true
155
+ create_table :users, force: args[:force] do |t|
156
+ t.string :jid, limit: 512, null: false
157
+ t.string :name, limit: 256, null: true
158
+ t.string :password, limit: 256, null: true
159
+ t.text :vcard, null: true
158
160
  end
159
- add_index :users, :jid, :unique => true
160
-
161
- create_table :contacts, :force => args[:force] do |t|
162
- t.integer :user_id, :null => false
163
- t.string :jid, :limit => 2048, :null => false
164
- t.string :name, :limit => 1000, :null => true
165
- t.string :ask, :limit => 1000, :null => true
166
- t.string :subscription, :limit => 1000, :null => false
161
+ add_index :users, :jid, unique: true
162
+
163
+ create_table :contacts, force: args[:force] do |t|
164
+ t.integer :user_id, null: false
165
+ t.string :jid, limit: 512, null: false
166
+ t.string :name, limit: 256, null: true
167
+ t.string :ask, limit: 128, null: true
168
+ t.string :subscription, limit: 128, null: false
167
169
  end
168
- add_index :contacts, [:user_id, :jid], :unique => true
170
+ add_index :contacts, [:user_id, :jid], unique: true
169
171
 
170
- create_table :groups, :force => args[:force] do |t|
171
- t.string :name, :limit => 1000, :null => false
172
+ create_table :groups, force: args[:force] do |t|
173
+ t.string :name, limit: 256, null: false
172
174
  end
173
- add_index :groups, :name, :unique => true
175
+ add_index :groups, :name, unique: true
174
176
 
175
- create_table :contacts_groups, :id => false, :force => args[:force] do |t|
176
- t.integer :contact_id, :null => false
177
- t.integer :group_id, :null => false
177
+ create_table :contacts_groups, id: false, force: args[:force] do |t|
178
+ t.integer :contact_id, null: false
179
+ t.integer :group_id, null: false
178
180
  end
179
- add_index :contacts_groups, [:contact_id, :group_id], :unique => true
181
+ add_index :contacts_groups, [:contact_id, :group_id], unique: true
180
182
 
181
- create_table :fragments, :force => args[:force] do |t|
182
- t.integer :user_id, :null => false
183
- t.string :root, :limit => 1000, :null => false
184
- t.string :namespace, :limit => 1000, :null => false
185
- t.text :xml, :null => false
183
+ create_table :fragments, force: args[:force] do |t|
184
+ t.integer :user_id, null: false
185
+ t.string :root, limit: 256, null: false
186
+ t.string :namespace, limit: 256, null: false
187
+ t.text :xml, null: false
186
188
  end
187
- add_index :fragments, [:user_id, :root, :namespace], :unique => true
189
+ add_index :fragments, [:user_id, :root, :namespace], unique: true
188
190
  end
189
191
  end
192
+ with_connection :create_schema, defer: false
190
193
 
191
194
  private
192
195
 
193
196
  def establish_connection
197
+ ActiveRecord::Base.logger = Logger.new('/dev/null')
194
198
  ActiveRecord::Base.establish_connection(@config)
195
199
  # has_and_belongs_to_many requires a connection so configure the
196
200
  # associations here rather than in the class definitions above.
data/lib/vines/storage.rb CHANGED
@@ -22,7 +22,7 @@ module Vines
22
22
 
23
23
  # Wrap a blocking IO method in a new method that pushes the original method
24
24
  # onto EventMachine's thread pool using EM#defer. Storage classes implemented
25
- # with blocking IO don't need to worry about threading or blocking the the
25
+ # with blocking IO don't need to worry about threading or blocking the
26
26
  # EventMachine reactor thread if they wrap their methods with this one.
27
27
  #
28
28
  # For example:
@@ -17,7 +17,8 @@ module Vines
17
17
  raise StreamErrors::NotAuthorized unless bind?(node)
18
18
  raise StreamErrors::PolicyViolation.new('max bind attempts reached') if @attempts > MAX_ATTEMPTS
19
19
  raise StanzaErrors::ResourceConstraint.new(node, 'wait') if resource_limit_reached?
20
- stream.user.jid.resource = resource(node)
20
+
21
+ stream.bind!(resource(node))
21
22
  doc = Document.new
22
23
  result = doc.create_element('iq', 'id' => node['id'], 'type' => 'result') do |el|
23
24
  el << doc.create_element('bind') do |bind|
@@ -25,7 +26,6 @@ module Vines
25
26
  bind << doc.create_element('jid', stream.user.jid.to_s)
26
27
  end
27
28
  end
28
-
29
29
  stream.write(result)
30
30
  send_empty_features
31
31
  advance
@@ -10,10 +10,11 @@ module Vines
10
10
  class Session
11
11
  include Comparable
12
12
 
13
- attr_accessor :domain, :last_broadcast_presence, :user
14
- attr_reader :id, :state
13
+ attr_accessor :domain, :user
14
+ attr_reader :id, :last_broadcast_presence, :state
15
15
 
16
16
  def initialize(stream)
17
+ @stream = stream
17
18
  @id = Kit.uuid
18
19
  @config = stream.config
19
20
  @state = Client::Start.new(stream)
@@ -45,8 +46,12 @@ module Vines
45
46
  !@user.nil?
46
47
  end
47
48
 
49
+ # Notify the session that the client has sent an initial presence
50
+ # broadcast and is now considered to be an "available" resource.
51
+ # Available resources are sent presence subscription stanzas.
48
52
  def available!
49
53
  @available = true
54
+ save_to_cluster
50
55
  end
51
56
 
52
57
  # An available resource has sent initial presence and can
@@ -55,6 +60,15 @@ module Vines
55
60
  @available && connected?
56
61
  end
57
62
 
63
+ # Complete resource binding with the given resource name, provided by the
64
+ # client or generated by the server. Once resource binding is completed,
65
+ # the stream is considered to be "connected" and ready for traffic.
66
+ def bind!(resource)
67
+ @user.jid.resource = resource
68
+ router << self
69
+ save_to_cluster
70
+ end
71
+
58
72
  # A connected resource has authenticated and bound a resource
59
73
  # identifier.
60
74
  def connected?
@@ -67,22 +81,38 @@ module Vines
67
81
  @requested_roster && connected?
68
82
  end
69
83
 
84
+ def last_broadcast_presence=(node)
85
+ @last_broadcast_presence = node
86
+ save_to_cluster
87
+ end
88
+
70
89
  def ready?
71
90
  @state.class == Client::Ready
72
91
  end
73
92
 
93
+ # Notify the session that the client has requested its roster and is now
94
+ # considered to be an "interested" resource. Interested resources are sent
95
+ # roster pushes when changes are made to their contacts.
74
96
  def requested_roster!
75
97
  @requested_roster = true
98
+ save_to_cluster
76
99
  end
77
100
 
78
101
  def stream_type
79
102
  :client
80
103
  end
81
104
 
82
- # Called by the stream when its disconnected from the client. The stream
105
+ def write(data)
106
+ @stream.write(data)
107
+ end
108
+
109
+ # Called by the stream when it's disconnected from the client. The stream
83
110
  # passes itself to this method in case multiple streams are accessing this
84
- # session.
111
+ # session (e.g. BOSH/HTTP).
85
112
  def unbind!(stream)
113
+ router.delete(self)
114
+ delete_from_cluster
115
+ unsubscribe_pubsub
86
116
  @unbound = true
87
117
  @available = false
88
118
  broadcast_unavailable
@@ -123,20 +153,22 @@ module Vines
123
153
 
124
154
  def broadcast_unavailable
125
155
  return unless authenticated?
126
-
156
+ Fiber.new do
157
+ broadcast(unavailable, available_subscribers)
158
+ broadcast(unavailable, router.available_resources(@user.jid, @user.jid))
159
+ remote_subscribers.each do |contact|
160
+ node = unavailable
161
+ node['to'] = contact.jid.bare.to_s
162
+ router.route(node) rescue nil # ignore RemoteServerNotFound
163
+ end
164
+ end.resume
165
+ end
166
+
167
+ def unavailable
127
168
  doc = Nokogiri::XML::Document.new
128
- el = doc.create_element('presence',
169
+ doc.create_element('presence',
129
170
  'from' => @user.jid.to_s,
130
171
  'type' => 'unavailable')
131
-
132
- broadcast(el, available_subscribers)
133
- broadcast(el, router.available_resources(@user.jid, @user.jid))
134
-
135
- remote_subscribers.each do |contact|
136
- node = el.clone
137
- node['to'] = contact.jid.bare.to_s
138
- router.route(node) rescue nil # ignore RemoteServerNotFound
139
- end
140
172
  end
141
173
 
142
174
  def broadcast(stanza, recipients)
@@ -147,7 +179,30 @@ module Vines
147
179
  end
148
180
 
149
181
  def router
150
- Router.instance
182
+ @config.router
183
+ end
184
+
185
+ def save_to_cluster
186
+ if @config.cluster?
187
+ @config.cluster.save_session(@user.jid, to_hash)
188
+ end
189
+ end
190
+
191
+ def delete_from_cluster
192
+ if connected? && @config.cluster?
193
+ @config.cluster.delete_session(@user.jid)
194
+ end
195
+ end
196
+
197
+ def unsubscribe_pubsub
198
+ if connected?
199
+ @config.vhosts[@user.jid.domain].unsubscribe_pubsub(@user.jid)
200
+ end
201
+ end
202
+
203
+ def to_hash
204
+ presence = @last_broadcast_presence ? @last_broadcast_presence.to_s : nil
205
+ {available: @available, interested: @requested_roster, presence: presence.to_s}
151
206
  end
152
207
  end
153
208
  end
@@ -11,6 +11,7 @@ module Vines
11
11
  def node(node)
12
12
  raise StreamErrors::NotAuthorized unless handshake?(node)
13
13
  stream.write('<handshake/>')
14
+ stream.router << stream
14
15
  advance
15
16
  end
16
17
 
@@ -10,8 +10,8 @@ module Vines
10
10
  to, from = stanza.validate_to, stanza.validate_from
11
11
  raise StreamErrors::ImproperAddressing unless to && from
12
12
  raise StreamErrors::InvalidFrom unless from.domain == stream.remote_domain
13
- stream.user = User.new(:jid => from)
14
- if stanza.local?
13
+ stream.user = User.new(jid: from)
14
+ if stanza.local? || stanza.to_pubsub_domain?
15
15
  stanza.process
16
16
  else
17
17
  stanza.route
@@ -22,6 +22,8 @@ module Vines
22
22
  def close
23
23
  Sessions.delete(@id)
24
24
  router.delete(self)
25
+ delete_from_cluster
26
+ unsubscribe_pubsub
25
27
  @requests.each {|req| req.stream.close_connection }
26
28
  @requests.clear
27
29
  @responses.clear
@@ -10,11 +10,6 @@ module Vines
10
10
  @session = Http::Session.new(self)
11
11
  end
12
12
 
13
- def post_init
14
- super
15
- router.delete(self)
16
- end
17
-
18
13
  # Override +Stream#create_parser+ to provide an HTTP parser rather than
19
14
  # a Nokogiri XML parser.
20
15
  def create_parser
@@ -96,7 +91,6 @@ module Vines
96
91
  raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns'] == NAMESPACES[:http_bind]
97
92
 
98
93
  Sessions[@session.id] = @session
99
- router << @session
100
94
  send_stream_header
101
95
  end
102
96
 
@@ -12,6 +12,7 @@ module Vines
12
12
  raise StreamErrors::NotAuthorized unless stream?(node)
13
13
  stream.start(node)
14
14
  stream.write('<stream:features/>')
15
+ stream.router << stream
15
16
  advance
16
17
  end
17
18
  end
@@ -11,6 +11,7 @@ module Vines
11
11
 
12
12
  def node(node)
13
13
  raise StreamErrors::NotAuthorized unless empty_features?(node)
14
+ stream.router << stream
14
15
  advance
15
16
  stream.notify_connected
16
17
  end
@@ -11,8 +11,12 @@ module Vines
11
11
  raise StreamErrors::ImproperAddressing unless to && from
12
12
  raise StreamErrors::InvalidFrom unless from.domain == stream.remote_domain
13
13
  raise StreamErrors::HostUnknown unless to.domain == stream.domain
14
- stream.user = User.new(:jid => from)
15
- stanza.process
14
+ stream.user = User.new(jid: from)
15
+ if stanza.local? || stanza.to_pubsub_domain?
16
+ stanza.process
17
+ else
18
+ stanza.route
19
+ end
16
20
  end
17
21
  end
18
22
  end
@@ -35,7 +35,8 @@ module Vines
35
35
 
36
36
  def self.connect(config, to, from, srv, callback)
37
37
  if srv.empty?
38
- callback.call(nil)
38
+ # fiber so storage calls work properly
39
+ Fiber.new { callback.call(nil) }.resume
39
40
  else
40
41
  begin
41
42
  rr = srv.shift
@@ -108,8 +109,8 @@ module Vines
108
109
  send_stream_header
109
110
  raise StreamErrors::UnsupportedVersion unless node['version'] == '1.0'
110
111
  raise StreamErrors::ImproperAddressing unless valid_address?(@domain) && valid_address?(@remote_domain)
111
- raise StreamErrors::HostUnknown unless config.vhost?(@domain)
112
- raise StreamErrors::NotAuthorized unless config.s2s?(@remote_domain)
112
+ raise StreamErrors::HostUnknown unless config.vhost?(@domain) || config.pubsub?(@domain) || config.component?(@domain)
113
+ raise StreamErrors::NotAuthorized unless config.s2s?(@remote_domain) && config.allowed?(@domain, @remote_domain)
113
114
  raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns'] == NAMESPACES[:server]
114
115
  raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns:stream'] == NAMESPACES[:stream]
115
116
  end
data/lib/vines/stream.rb CHANGED
@@ -18,7 +18,6 @@ module Vines
18
18
  end
19
19
 
20
20
  def post_init
21
- router << self
22
21
  @remote_addr, @local_addr = addresses
23
22
  @user, @closed, @stanza_size = nil, false, 0
24
23
  @bucket = TokenBucket.new(100, 10)
@@ -67,8 +66,12 @@ module Vines
67
66
  # Returns the storage system for the domain. If no domain is given,
68
67
  # the stream's storage mechanism is returned.
69
68
  def storage(domain=nil)
70
- host = @config.vhosts[domain || self.domain]
71
- host.storage if host
69
+ @config.storage(domain || self.domain)
70
+ end
71
+
72
+ # Returns the Vines::Config::Host virtual host for the stream's domain.
73
+ def vhost
74
+ @config.vhosts[domain]
72
75
  end
73
76
 
74
77
  # Reload the user's information into their active connections. Call this
@@ -156,7 +159,7 @@ module Vines
156
159
  end
157
160
 
158
161
  def router
159
- Router.instance
162
+ @config.router
160
163
  end
161
164
 
162
165
  private
@@ -236,9 +239,10 @@ module Vines
236
239
  %w[crt key].map {|ext| File.join(VINES_ROOT, 'conf', 'certs', "#{domain}.#{ext}") }
237
240
  end
238
241
 
242
+ # Return true if this is a valid domain-only JID that can be used in
243
+ # stream initiation stanza headers.
239
244
  def valid_address?(jid)
240
- jid = JID.new(jid) rescue nil
241
- jid && !jid.empty?
245
+ JID.new(jid).domain? rescue false
242
246
  end
243
247
  end
244
248
  end
data/lib/vines/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Vines
4
- VERSION = '0.3.2'
4
+ VERSION = '0.4.0'
5
5
  end