diaspora-vines 0.1.22 → 0.1.24

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.
@@ -18,12 +18,14 @@ module Vines
18
18
  class AspectMembership < ActiveRecord::Base
19
19
  belongs_to :aspect
20
20
  belongs_to :contact
21
-
21
+
22
22
  has_one :users, :through => :contact
23
23
  has_one :person, :through => :contact
24
24
  end
25
25
 
26
26
  class Contact < ActiveRecord::Base
27
+ scope :chat_enabled, -> { joins(:aspects).where("aspects.chat_enabled = ?", true) }
28
+
27
29
  belongs_to :users
28
30
  belongs_to :person
29
31
 
@@ -44,7 +46,7 @@ module Vines
44
46
 
45
47
  serialize :groups, JSON
46
48
  end
47
-
49
+
48
50
  class ChatFragment < ActiveRecord::Base
49
51
  belongs_to :users
50
52
  end
@@ -69,7 +71,7 @@ module Vines
69
71
  unless defined? Rails
70
72
  raise "You configured diaspora-sql adapter without Diaspora environment"
71
73
  end
72
-
74
+
73
75
  config = Rails.application.config.database_configuration[Rails.env]
74
76
  %w[adapter database host port username password].each do |key|
75
77
  @config[key.to_sym] = config[key]
@@ -93,7 +95,7 @@ module Vines
93
95
  xuser.authentication_token
94
96
 
95
97
  # add diaspora contacts
96
- xuser.contacts.each do |contact|
98
+ xuser.contacts.chat_enabled.each do |contact|
97
99
  handle = contact.person.diaspora_handle
98
100
  ask, subscription, groups = get_diaspora_flags(contact)
99
101
  user.roster << Vines::Contact.new(
@@ -121,7 +123,7 @@ module Vines
121
123
  def authenticate(username, password)
122
124
  user = find_user(username)
123
125
 
124
- pepper = "#{password}#{Config.instance.pepper}" rescue password
126
+ pepper = "#{password}#{Devise.pepper}" rescue password
125
127
  dbhash = BCrypt::Password.new(user.password) rescue nil
126
128
  hash = BCrypt::Engine.hash_secret(pepper, dbhash.salt) rescue nil
127
129
 
data/lib/vines/store.rb CHANGED
@@ -55,16 +55,25 @@ module Vines
55
55
  @@sources ||= begin
56
56
  pattern = /-{5}BEGIN CERTIFICATE-{5}\n.*?-{5}END CERTIFICATE-{5}\n/m
57
57
  files = Dir[File.join(@dir, '*.crt')]
58
- files << AppConfig.environment.certificate_authorities if defined?(AppConfig)
58
+ if defined?(AppConfig)
59
+ chain = AppConfig.environment.certificate_authorities.get
60
+ files << chain unless chain.nil?
61
+ end
59
62
  pairs = files.map do |name|
60
- File.open(name, "r:UTF-8") do |f|
61
- pems = f.read.scan(pattern)
62
- certs = pems.map {|pem| OpenSSL::X509::Certificate.new(pem) }
63
- certs.reject! {|cert| cert.not_after < Time.now }
64
- [name, certs]
63
+ begin
64
+ File.open(name, "r:UTF-8") do |f|
65
+ pems = f.read.scan(pattern)
66
+ certs = pems.map {|pem| OpenSSL::X509::Certificate.new(pem) }
67
+ certs.reject! {|cert| cert.not_after < Time.now }
68
+ [name, certs]
69
+ end
70
+ rescue ArgumentError => e
71
+ puts "Skipping '#{name}' cause of '#{e.message.to_s}'! "+
72
+ "Checkout https://wiki.diasporafoundation.org/Vines#FAQ "+
73
+ "for further instructions."
65
74
  end
66
75
  end
67
- Hash[pairs]
76
+ Hash[pairs.compact]
68
77
  end
69
78
  @@sources.values.flatten
70
79
  end
@@ -119,6 +128,9 @@ module Vines
119
128
  end
120
129
  end
121
130
  end
131
+ puts "Your're using vines without a certificate! "+
132
+ "Checkout https://wiki.diasporafoundation.org/Vines#Certificates "+
133
+ "for further instructions."
122
134
  nil
123
135
  end
124
136
 
data/lib/vines/stream.rb CHANGED
@@ -17,6 +17,11 @@ module Vines
17
17
  @config = config
18
18
  end
19
19
 
20
+ # Initialize the stream after its connection to the server has completed.
21
+ # EventMachine calls this method when an incoming connection is accepted
22
+ # into the event loop.
23
+ #
24
+ # Returns nothing.
20
25
  def post_init
21
26
  @remote_addr, @local_addr = addresses
22
27
  @user, @closed, @stanza_size = nil, false, 0
@@ -33,23 +38,34 @@ module Vines
33
38
  # stream is first connected as well as for stream restarts during
34
39
  # negotiation. Subclasses can override this method to provide a different
35
40
  # type of parser (e.g. HTTP).
41
+ #
42
+ # Returns nothing.
36
43
  def create_parser
37
- @parser = Parser.new.tap do |p|
38
- p.stream_open {|node| @nodes.push(node) }
39
- p.stream_close { close_connection }
40
- p.stanza {|node| @nodes.push(node) }
44
+ @parser = Parser.new.tap do |parser|
45
+ parser.stream_open {|node| @nodes.push(node) }
46
+ parser.stream_close { close_connection }
47
+ parser.stanza {|node| @nodes.push(node) }
41
48
  end
42
49
  end
43
50
 
44
- # Advance the state machine into the +Closed+ state so any remaining queued
51
+ # Advance the state machine into the `Closed` state so any remaining queued
45
52
  # nodes are not processed while we're waiting for EM to actually close the
46
53
  # connection.
54
+ #
55
+ # Returns nothing.
47
56
  def close_connection(after_writing=false)
48
57
  super
49
58
  @closed = true
50
59
  advance(Client::Closed.new(self))
51
60
  end
52
61
 
62
+ # Read bytes off the stream and feed them into the XML parser. EventMachine
63
+ # is responsible for calling this method on its event loop as connections
64
+ # become readable.
65
+ #
66
+ # data - The byte String sent to the server from the client, hopefully XML.
67
+ #
68
+ # Returns nothing.
53
69
  def receive_data(data)
54
70
  return if @closed
55
71
  @stanza_size += data.bytesize
@@ -62,6 +78,8 @@ module Vines
62
78
 
63
79
  # Reset the connection's XML parser when a new <stream:stream> header
64
80
  # is received.
81
+ #
82
+ # Returns nothing.
65
83
  def reset
66
84
  create_parser
67
85
  end
@@ -72,7 +90,7 @@ module Vines
72
90
  @config.storage(domain || self.domain)
73
91
  end
74
92
 
75
- # Returns the Vines::Config::Host virtual host for the stream's domain.
93
+ # Returns the Config::Host virtual host for the stream's domain.
76
94
  def vhost
77
95
  @config.vhost(domain)
78
96
  end
@@ -80,6 +98,10 @@ module Vines
80
98
  # Reload the user's information into their active connections. Call this
81
99
  # after storage.save_user() to sync the new user state with their other
82
100
  # connections.
101
+ #
102
+ # user - The User whose connection info needs refreshing.
103
+ #
104
+ # Returns nothing.
83
105
  def update_user_streams(user)
84
106
  connected_resources(user.jid.bare).each do |stream|
85
107
  stream.user.update_from(user)
@@ -98,13 +120,26 @@ module Vines
98
120
  router.interested_resources(*jid, user.jid)
99
121
  end
100
122
 
101
- def ssl_verify_peer(pem); true; end
123
+ def ssl_verify_peer(pem)
124
+ # Skip verifying if user accept self-signed certificates
125
+ return if self.vhost.accept_self_signed?
126
+ # EM is supposed to close the connection when this returns false,
127
+ # but it only does that for inbound connections, not when we
128
+ # make a connection to another server.
129
+ @store.trusted?(pem).tap do |trusted|
130
+ close_connection unless trusted
131
+ end
132
+ end
102
133
 
103
134
  def cert_domain_matches?(domain)
104
135
  @store.domain?(get_peer_cert, domain)
105
136
  end
106
137
 
107
138
  # Send the data over the wire to this client.
139
+ #
140
+ # data - The XML String or XML::Node to write to the socket.
141
+ #
142
+ # Returns nothing.
108
143
  def write(data)
109
144
  log_node(data, :out)
110
145
  if data.respond_to?(:to_xml)
@@ -132,7 +167,11 @@ module Vines
132
167
  end
133
168
 
134
169
  # Advance the stream's state machine to the new state. XML nodes received
135
- # by the stream will be passed to this state's +node+ method.
170
+ # by the stream will be passed to this state's `node` method.
171
+ #
172
+ # state - The Stream::State to process the stanzas next.
173
+ #
174
+ # Returns the new Stream::State.
136
175
  def advance(state)
137
176
  @state = state
138
177
  end
@@ -140,6 +179,10 @@ module Vines
140
179
  # Stream level errors close the stream while stanza and SASL errors are
141
180
  # written to the client and leave the stream open. All exceptions should
142
181
  # pass through this method for consistent handling.
182
+ #
183
+ # e - The StandardError, usually XmppError, that occurred.
184
+ #
185
+ # Returns nothing.
143
186
  def error(e)
144
187
  case e
145
188
  when SaslError, StanzaError
@@ -160,7 +203,9 @@ module Vines
160
203
 
161
204
  private
162
205
 
163
- # Return the remote and local socket addresses used by this connection.
206
+ # Determine the remote and local socket addresses used by this connection.
207
+ #
208
+ # Returns a two-element Array of String addresses.
164
209
  def addresses
165
210
  [get_peername, get_sockname].map do |addr|
166
211
  addr ? Socket.unpack_sockaddr_in(addr)[0, 2].reverse.join(':') : 'unknown'
@@ -169,12 +214,21 @@ module Vines
169
214
 
170
215
  # Write the StreamError's xml to the stream. Subclasses can override
171
216
  # this method with custom error writing behavior.
217
+ #
218
+ # A call to `close_stream` should follow this method. Stream level errors
219
+ # are fatal to the connection.
220
+ #
221
+ # e - The StreamError that caused the connection to close.
222
+ #
223
+ # Returns nothing.
172
224
  def send_stream_error(e)
173
225
  write(e.to_xml)
174
226
  end
175
227
 
176
- # Write a closing stream tag to the stream then close the stream. Subclasses
177
- # can override this method for custom close behavior.
228
+ # Write a closing stream tag and close the connection. Subclasses can
229
+ # override this method for custom close behavior.
230
+ #
231
+ # Returns nothing.
178
232
  def close_stream
179
233
  write('</stream:stream>')
180
234
  close_connection_after_writing
@@ -187,7 +241,14 @@ module Vines
187
241
 
188
242
  # Schedule a queue pop on the EM thread to handle the next element. This
189
243
  # guarantees all stanzas received on this stream are processed in order.
190
- # http://tools.ietf.org/html/rfc6120#section-10.1
244
+ #
245
+ # http://tools.ietf.org/html/rfc6120#section-10.1
246
+ #
247
+ # Once a node is processed, this method recursively schedules itself to pop
248
+ # the next node and so on. A single call to this method effectively begins
249
+ # an asynchronous node processing loop.
250
+ #
251
+ # Returns nothing.
191
252
  def process_node_queue
192
253
  @nodes.pop do |node|
193
254
  Fiber.new do
@@ -225,14 +286,20 @@ module Vines
225
286
  ["#{label} stanza:".ljust(PAD), from, to, node])
226
287
  end
227
288
 
228
- # Returns the current +State+ of the stream's state machine. Provided as a
289
+ # Inspects the current state of the stream's state machine. Provided as a
229
290
  # method so subclasses can override the behavior.
291
+ #
292
+ # Returns the current Stream::State.
230
293
  def state
231
294
  @state
232
295
  end
233
296
 
234
- # Return +true+ if this is a valid domain-only JID that can be used in
297
+ # Determine if this is a valid domain-only JID that can be used in
235
298
  # stream initiation stanza headers.
299
+ #
300
+ # jid - The String or JID to verify (e.g. 'wonderland.lit').
301
+ #
302
+ # Return true if the jid is domain-only.
236
303
  def valid_address?(jid)
237
304
  JID.new(jid).domain? rescue false
238
305
  end
@@ -60,21 +60,25 @@ module Vines
60
60
 
61
61
  private
62
62
 
63
- # The +to+ domain address set on the initial stream header must not change
63
+ # The `to` domain address set on the initial stream header must not change
64
64
  # during stream restarts. This prevents a user from authenticating in one
65
65
  # domain, then using a stream in a different domain.
66
+ #
67
+ # to - The String domain JID to verify (e.g. 'wonderland.lit').
68
+ #
69
+ # Returns true if the client connection is misbehaving and should be closed.
66
70
  def domain_change?(to)
67
71
  to != @session.domain
68
72
  end
69
73
 
70
74
  def send_stream_header(to)
71
75
  attrs = {
72
- 'xmlns' => NAMESPACES[:client],
76
+ 'xmlns' => NAMESPACES[:client],
73
77
  'xmlns:stream' => NAMESPACES[:stream],
74
- 'xml:lang' => 'en',
75
- 'id' => Kit.uuid,
76
- 'from' => @session.domain,
77
- 'version' => '1.0'
78
+ 'xml:lang' => 'en',
79
+ 'id' => Kit.uuid,
80
+ 'from' => @session.domain,
81
+ 'version' => '1.0'
78
82
  }
79
83
  attrs['to'] = to if to
80
84
  write "<stream:stream %s>" % attrs.to_a.map{|k,v| "#{k}='#{v}'"}.join(' ')
@@ -46,10 +46,10 @@ module Vines
46
46
 
47
47
  def send_stream_header
48
48
  attrs = {
49
- 'xmlns' => NAMESPACES[:component],
49
+ 'xmlns' => NAMESPACES[:component],
50
50
  'xmlns:stream' => NAMESPACES[:stream],
51
- 'id' => @stream_id,
52
- 'from' => @remote_domain
51
+ 'id' => @stream_id,
52
+ 'from' => @remote_domain
53
53
  }
54
54
  write "<stream:stream %s>" % attrs.to_a.map{|k,v| "#{k}='#{v}'"}.join(' ')
55
55
  end
@@ -10,22 +10,28 @@ module Vines
10
10
  @session = Http::Session.new(self)
11
11
  end
12
12
 
13
- # Override +Stream#create_parser+ to provide an HTTP parser rather than
13
+ # Override Stream#create_parser to provide an HTTP parser rather than
14
14
  # a Nokogiri XML parser.
15
+ #
16
+ # Returns nothing.
15
17
  def create_parser
16
- @parser = ::Http::Parser.new.tap do |p|
18
+ @parser = ::Http::Parser.new.tap do |parser|
17
19
  body = ''
18
- p.on_body = proc {|data| body << data }
19
- p.on_message_complete = proc {
20
+ parser.on_body = proc {|data| body << data }
21
+ parser.on_message_complete = proc do
20
22
  process_request(Request.new(self, @parser, body))
21
23
  body = ''
22
- }
24
+ end
23
25
  end
24
26
  end
25
27
 
26
28
  # If the session ID is valid, switch this stream's session to the new
27
29
  # ID and return true. Some clients, like Google Chrome, reuse one stream
28
30
  # for multiple sessions.
31
+ #
32
+ # sid - The String session ID.
33
+ #
34
+ # Returns true if the server previously distributed this SID to a client.
29
35
  def valid_session?(sid)
30
36
  if session = Sessions[sid]
31
37
  @session = session
@@ -62,13 +68,27 @@ module Vines
62
68
 
63
69
  # Override Stream#write to queue stanzas rather than immediately writing
64
70
  # to the stream. Stanza responses must be paired with a queued request.
71
+ #
72
+ # If a request is not waiting, the written stanzas will buffer until they
73
+ # can be sent in the next response.
74
+ #
75
+ # data - The XML String or XML::Node to write to the HTTP socket.
76
+ #
77
+ # Returns nothing.
65
78
  def write(data)
66
79
  @session.write(data)
67
80
  end
68
81
 
69
- # Return an array of Node objects inside the body element.
82
+ # Parse the one or more stanzas from a single body element. BOSH clients
83
+ # buffer stanzas sent in quick succession, and send them as a bundle, to
84
+ # save on the request/response cycle.
85
+ #
70
86
  # TODO This parses the XML again just to strip namespaces. Figure out
71
87
  # Nokogiri namespace handling instead.
88
+ #
89
+ # body - The XML::Node containing the BOSH `body` element.
90
+ #
91
+ # Returns an Array of XML::Node stanzas.
72
92
  def parse_body(body)
73
93
  body.namespace = nil
74
94
  body.elements.map do |node|
@@ -133,8 +153,12 @@ module Vines
133
153
  @session.reply(node)
134
154
  end
135
155
 
136
- # Override +Stream#send_stream_error+ to wrap the error XML in a BOSH
156
+ # Override Stream#send_stream_error to wrap the error XML in a BOSH
137
157
  # terminate body tag.
158
+ #
159
+ # e - The StreamError that caused the connection to close.
160
+ #
161
+ # Returns nothing.
138
162
  def send_stream_error(e)
139
163
  doc = Nokogiri::XML::Document.new
140
164
  node = doc.create_element('body',
@@ -146,12 +170,14 @@ module Vines
146
170
  @session.reply(node)
147
171
  end
148
172
 
149
- # Override +Stream#close_stream+ to simply close the connection without
173
+ # Override Stream#close_stream to simply close the connection without
150
174
  # writing a closing stream tag.
175
+ #
176
+ # Returns nothing.
151
177
  def close_stream
152
178
  close_connection_after_writing
153
179
  @session.close
154
180
  end
155
181
  end
156
182
  end
157
- end
183
+ end
@@ -25,13 +25,22 @@ module Vines
25
25
 
26
26
  attr_reader :stream, :body, :headers, :method, :path, :url, :query
27
27
 
28
+ # Create a new request parsed from an HTTP client connection. We'll try
29
+ # to keep this request open until there are stanzas available to send
30
+ # as a response.
31
+ #
32
+ # stream - The Stream::Http client connection that received the request.
33
+ # parser - The Http::Parser that parsed the HTTP request.
34
+ # body - The String request body.
28
35
  def initialize(stream, parser, body)
29
- @stream, @body = stream, body
36
+ uri = URI(parser.request_url)
37
+ @stream = stream
38
+ @body = body
30
39
  @headers = parser.headers
31
40
  @method = parser.http_method
32
- @path = parser.request_path
33
41
  @url = parser.request_url
34
- @query = parser.query_string
42
+ @path = uri.path
43
+ @query = uri.query
35
44
  @received = Time.now
36
45
  end
37
46
 
@@ -44,10 +53,12 @@ module Vines
44
53
  # directory. Take care to prevent directory traversal attacks with paths
45
54
  # like ../../../etc/passwd. Use the If-Modified-Since request header
46
55
  # to implement caching.
56
+ #
57
+ # Returns nothing.
47
58
  def reply_with_file(dir)
48
59
  path = File.expand_path(File.join(dir, @path))
49
60
 
50
- # redirect requests missing a slash so relative links work
61
+ # Redirect requests missing a slash so relative links work.
51
62
  if File.directory?(path) && !@path.end_with?('/')
52
63
  send_status(301, MOVED, "Location: #{redirect_uri}")
53
64
  return
@@ -69,6 +80,8 @@ module Vines
69
80
 
70
81
  # Send an HTTP 200 OK response wrapping the XMPP node content back
71
82
  # to the client.
83
+ #
84
+ # Returns nothing.
72
85
  def reply(node, content_type)
73
86
  body = node.to_s
74
87
  header = [
@@ -90,6 +103,8 @@ module Vines
90
103
  # Send a 200 OK response, allowing any origin domain to connect to the
91
104
  # server, in response to CORS preflight OPTIONS requests. This allows
92
105
  # any web application using strophe.js to connect to our BOSH port.
106
+ #
107
+ # Returns nothing.
93
108
  def reply_to_options
94
109
  allow = @headers['Access-Control-Request-Headers']
95
110
  headers = [
@@ -107,6 +122,8 @@ module Vines
107
122
  # wasn't sent by the client, just return the relative path that
108
123
  # was requested. The Location response header must contain the fully
109
124
  # qualified URI, but most browsers will accept relative paths as well.
125
+ #
126
+ # Returns the String URL.
110
127
  def redirect_uri
111
128
  host = headers['Host']
112
129
  uri = "#{path}/"
@@ -137,6 +154,8 @@ module Vines
137
154
  # Stream the contents of the file to the client in a 200 OK response.
138
155
  # Send a Last-Modified response header so clients can send us an
139
156
  # If-Modified-Since request header for caching.
157
+ #
158
+ # Returns nothing.
140
159
  def send_file(path, status=200, message='OK')
141
160
  header = [
142
161
  "HTTP/1.1 #{status} #{message}",
@@ -162,6 +181,8 @@ module Vines
162
181
  # HTTP server. Reverse proxy servers (nginx/apache) can use this cookie
163
182
  # to implement sticky sessions. Return nil if vroute was not set in
164
183
  # config.rb and no cookie should be sent.
184
+ #
185
+ # Returns a String cookie value or nil if disabled.
165
186
  def vroute_cookie
166
187
  route = @stream.config[:http].vroute
167
188
  route ? "Set-Cookie: vroute=#{route}; path=/; HttpOnly" : nil
@@ -169,4 +190,4 @@ module Vines
169
190
  end
170
191
  end
171
192
  end
172
- end
193
+ end