vines 0.3.2 → 0.4.0

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