vines 0.4.9 → 0.4.10

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4a444c695d79a88f165a9f86f2989eb27e128e79
4
- data.tar.gz: 3b271ce86f7ada201e8788daef5d8c207190615b
3
+ metadata.gz: 377b65dfeb6ebd0ed33d7dcd0ddb99e125114597
4
+ data.tar.gz: 0a3addc473971d37aad7c4011909e22162fcf382
5
5
  SHA512:
6
- metadata.gz: 8c258a7e3237ec24854ade1d9ac66de591440c48635e4a184a89abe0bf9171f4b48fea23f391fe665da6938a2d0c2964f4b0beb99e4f8959f1e225477fec0fa7
7
- data.tar.gz: 30f2397168e45ac1e8ed911b6110750b2deb823f64e1f0908a60355f5a6732d4d938fa370945156b18c662e4091f8cb88653065a073c79dd7c1ebe728a48a1b8
6
+ metadata.gz: a10339115953bedabc5b5fae4ec56cd4281f4ac9ecc9b5484a870826f687238240d48350bd7b1dadc99f5d88b6bf5710572542794a74c3332dc8fe15a3a0ba5d
7
+ data.tar.gz: 8d65071c1112f5df81cbb347060f9a416520809f4d794b99bab6a1cfee4bc2c532ff299fa2fb8a8bb793df942dd56253351336189772de7d0a674169693213ff
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Welcome to Vines
1
+ # Vines XMPP Server
2
2
 
3
3
  Vines is an XMPP chat server that supports thousands of simultaneous connections,
4
4
  using EventMachine for asynchronous IO. User data is stored in a
@@ -72,6 +72,7 @@ end
72
72
  resolv
73
73
  set
74
74
  socket
75
+ uri
75
76
  yaml
76
77
 
77
78
  vines/cli
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Vines
4
4
  class Cluster
5
- # Subscribes to the redis nodes:all broadcast channel to listen for
5
+ # Subscribes to the redis `nodes:all` broadcast channel to listen for
6
6
  # heartbeats from other cluster members. Also subscribes to a channel
7
7
  # exclusively for this particular node, listening for stanzas routed to us
8
8
  # from other nodes.
@@ -22,6 +22,8 @@ module Vines
22
22
  # Create a new redis connection and subscribe to the nodes:all broadcast
23
23
  # channel as well as the channel for this cluster node. Redis connections
24
24
  # in subscribe mode cannot be used for other key/value operations.
25
+ #
26
+ # Returns nothing.
25
27
  def subscribe
26
28
  conn = @cluster.connect
27
29
  conn.subscribe(ALL)
@@ -35,6 +37,8 @@ module Vines
35
37
 
36
38
  # Recursively process incoming messages from the queue, guaranteeing they
37
39
  # are processed in the order they are received.
40
+ #
41
+ # Returns nothing.
38
42
  def process_messages
39
43
  @messages.pop do |channel, message|
40
44
  Fiber.new do
@@ -46,6 +50,11 @@ module Vines
46
50
 
47
51
  # Process messages as they arrive on the pubsub channels to which we're
48
52
  # subscribed.
53
+ #
54
+ # channel - The String channel name on which the message was received.
55
+ # message - The JSON formatted message String.
56
+ #
57
+ # Returns nothing.
49
58
  def on_message(channel, message)
50
59
  doc = JSON.parse(message)
51
60
  case channel
@@ -56,9 +65,13 @@ module Vines
56
65
  log.error("Cluster subscription message failed: #{e}")
57
66
  end
58
67
 
59
- # Process a message sent to the nodes:all broadcast channel. In the case
68
+ # Process a message sent to the `nodes:all` broadcast channel. In the case
60
69
  # of node heartbeats, we update the last time we heard from this node so
61
70
  # we can cleanup its session if it goes offline.
71
+ #
72
+ # message - The parsed Hash of received message data.
73
+ #
74
+ # Returns nothing.
62
75
  def to_all(message)
63
76
  case message[TYPE]
64
77
  when ONLINE, HEARTBEAT
@@ -71,6 +84,10 @@ module Vines
71
84
  # Process a message published to this node's channel. Messages sent to
72
85
  # this channel are stanzas that need to be routed to connections attached
73
86
  # to this node.
87
+ #
88
+ # message - The parsed Hash of received message data.
89
+ #
90
+ # Returns nothing.
74
91
  def to_node(message)
75
92
  case message[TYPE]
76
93
  when STANZA then route_stanza(message)
@@ -80,6 +97,10 @@ module Vines
80
97
 
81
98
  # Send the stanza, from a remote cluster node, to locally connected
82
99
  # streams for the destination user.
100
+ #
101
+ # message - The parsed Hash of received message data.
102
+ #
103
+ # Returns nothing.
83
104
  def route_stanza(message)
84
105
  node = Nokogiri::XML(message[STANZA]).root rescue nil
85
106
  return unless node
@@ -95,6 +116,10 @@ module Vines
95
116
 
96
117
  # Update the roster information, that's cached in locally connected
97
118
  # streams, for this user.
119
+ #
120
+ # message - The parsed Hash of received message data.
121
+ #
122
+ # Returns nothing.
98
123
  def update_user(message)
99
124
  jid = JID.new(message['jid']).bare
100
125
  if user = @cluster.storage(jid.domain).find_user(jid)
@@ -10,6 +10,10 @@ module Vines
10
10
 
11
11
  # Register a nickname that can be used in the config file to specify this
12
12
  # storage implementation.
13
+ #
14
+ # name - The String name for this storage backend.
15
+ #
16
+ # Returns nothing.
13
17
  def self.register(name)
14
18
  @@nicks[name.to_sym] = self
15
19
  end
@@ -25,15 +29,18 @@ module Vines
25
29
  # with blocking IO don't need to worry about threading or blocking the
26
30
  # EventMachine reactor thread if they wrap their methods with this one.
27
31
  #
28
- # For example:
29
- # def find_user(jid)
30
- # some_blocking_lookup(jid)
31
- # end
32
- # defer :find_user
32
+ # Examples
33
+ #
34
+ # def find_user(jid)
35
+ # some_blocking_lookup(jid)
36
+ # end
37
+ # defer :find_user
33
38
  #
34
39
  # Storage classes that use asynchronous IO (through an EventMachine
35
40
  # enabled library like em-http-request or em-redis) don't need any special
36
41
  # consideration and must not use this method.
42
+ #
43
+ # Returns nothing.
37
44
  def self.defer(method)
38
45
  old = instance_method(method)
39
46
  define_method method do |*args|
@@ -50,11 +57,14 @@ module Vines
50
57
  # authenticate method as usual. This allows storage classes to implement
51
58
  # their native authentication logic and not worry about handling LDAP.
52
59
  #
53
- # For example:
54
- # def authenticate(username, password)
55
- # some_user_lookup_by_password(username, password)
56
- # end
57
- # wrap_ldap :authenticate
60
+ # Examples
61
+ #
62
+ # def authenticate(username, password)
63
+ # some_user_lookup_by_password(username, password)
64
+ # end
65
+ # wrap_ldap :authenticate
66
+ #
67
+ # Returns nothing.
58
68
  def self.wrap_ldap(method)
59
69
  old = instance_method(method)
60
70
  define_method method do |*args|
@@ -64,21 +74,24 @@ module Vines
64
74
 
65
75
  # Wrap a method with Fiber yield and resume logic. The method must yield
66
76
  # its result to a block. This makes it easier to write asynchronous
67
- # implementations of +authenticate+, +find_user+, and +save_user+ that
77
+ # implementations of `authenticate`, `find_user`, and `save_user` that
68
78
  # block and return a result rather than yielding.
69
79
  #
70
- # For example:
71
- # def find_user(jid)
72
- # http = EM::HttpRequest.new(url).get
73
- # http.callback { yield build_user_from_http_response(http) }
74
- # end
75
- # fiber :find_user
80
+ # Examples
76
81
  #
77
- # Because +find_user+ has been wrapped in Fiber logic, we can call it
82
+ # def find_user(jid)
83
+ # http = EM::HttpRequest.new(url).get
84
+ # http.callback { yield build_user_from_http_response(http) }
85
+ # end
86
+ # fiber :find_user
87
+ #
88
+ # Because `find_user` has been wrapped in Fiber logic, we can call it
78
89
  # synchronously even though it uses asynchronous EventMachine calls.
79
90
  #
80
- # user = storage.find_user('alice@wonderland.lit')
81
- # puts user.nil?
91
+ # user = storage.find_user('alice@wonderland.lit')
92
+ # puts user.nil?
93
+ #
94
+ # Returns nothing.
82
95
  def self.fiber(method)
83
96
  old = instance_method(method)
84
97
  define_method method do |*args|
@@ -90,21 +103,26 @@ module Vines
90
103
  end
91
104
  end
92
105
 
93
- # Return +true+ if users are authenticated against an LDAP directory.
106
+ # Return true if users are authenticated against an LDAP directory.
94
107
  def ldap?
95
108
  !!ldap
96
109
  end
97
110
 
98
- # Validate the username and password pair and return a +Vines::User+ object
99
- # on success. Return +nil+ on failure.
111
+ # Validate the username and password pair.
112
+ #
113
+ # username - The String login JID to verify.
114
+ # password - The String password the user presented to the server.
100
115
  #
101
- # For example:
102
- # user = storage.authenticate('alice@wonderland.lit', 'secr3t')
103
- # puts user.nil?
116
+ # Examples
117
+ #
118
+ # user = storage.authenticate('alice@wonderland.lit', 'secr3t')
119
+ # puts user.nil?
104
120
  #
105
121
  # This default implementation validates the password against a bcrypt hash
106
122
  # of the password stored in the database. Sub-classes not using bcrypt
107
123
  # passwords must override this method.
124
+ #
125
+ # Returns a Vines::User object on success, nil on failure.
108
126
  def authenticate(username, password)
109
127
  user = find_user(username)
110
128
  hash = BCrypt::Password.new(user.password) rescue nil
@@ -112,92 +130,141 @@ module Vines
112
130
  end
113
131
  wrap_ldap :authenticate
114
132
 
115
- # Return the +Vines::User+ associated with the JID. Return +nil+ if the user
116
- # could not be found. JID may be +nil+, a +String+, or a +Vines::JID+
117
- # object. It may be a bare JID or a full JID. Implementations of this method
118
- # must convert the JID to a bare JID before searching for the user in the
119
- # database.
133
+ # Find the user in the storage database by their unique JID.
120
134
  #
121
- # user = storage.find_user('alice@wonderland.lit')
122
- # puts user.nil?
135
+ # jid - The String or JID of the user, possibly nil. This may be either a
136
+ # bare JID or full JID. Implementations of this method must convert
137
+ # the JID to a bare JID before searching for the user in the database.
138
+ #
139
+ # Examples
140
+ #
141
+ # # Bare JID lookup.
142
+ # user = storage.find_user('alice@wonderland.lit')
143
+ # puts user.nil?
144
+ #
145
+ # # Full JID lookup.
146
+ # user = storage.find_user('alice@wonderland.lit/tea')
147
+ # puts user.nil?
148
+ #
149
+ # Returns the User identified by the JID, nil if not found.
123
150
  def find_user(jid)
124
151
  raise 'subclass must implement'
125
152
  end
126
153
 
127
- # Persist the +Vines::User+ object to the database and return when the save
128
- # is complete.
154
+ # Persist the user to the database, and return when the save is complete.
155
+ #
156
+ # user - The User to persist.
129
157
  #
130
- # alice = Vines::User.new(:jid => 'alice@wonderland.lit')
131
- # storage.save_user(alice)
132
- # puts 'saved'
158
+ # Examples
159
+ #
160
+ # alice = Vines::User.new(jid: 'alice@wonderland.lit')
161
+ # storage.save_user(alice)
162
+ # puts 'saved'
163
+ #
164
+ # Returns nothing.
133
165
  def save_user(user)
134
166
  raise 'subclass must implement'
135
167
  end
136
168
 
137
- # Return the Nokogiri::XML::Node for the vcard stored for this JID. Return
138
- # nil if the vcard could not be found. JID may be +nil+, a +String+, or a
139
- # +Vines::JID+ object. It may be a bare JID or a full JID. Implementations
140
- # of this method must convert the JID to a bare JID before searching for the
141
- # vcard in the database.
169
+ # Find the user's vcard by their unique JID.
170
+ #
171
+ # jid - The String or JID of the user, possibly nil. This may be either a
172
+ # bare JID or full JID. Implementations of this method must convert
173
+ # the JID to a bare JID before searching for the vcard in the database.
174
+ #
175
+ # Examples
176
+ #
177
+ # card = storage.find_vcard('alice@wonderland.lit')
178
+ # puts card.nil?
142
179
  #
143
- # card = storage.find_vcard('alice@wonderland.lit')
144
- # puts card.nil?
180
+ # Returns the vcard's Nokogiri::XML::Node, nil if not found.
145
181
  def find_vcard(jid)
146
182
  raise 'subclass must implement'
147
183
  end
148
184
 
149
- # Save the vcard to the database and return when the save is complete. JID
150
- # may be a +String+ or a +Vines::JID+ object. It may be a bare JID or a
151
- # full JID. Implementations of this method must convert the JID to a bare
152
- # JID before saving the vcard. Card is a +Nokogiri::XML::Node+ object.
185
+ # Save the vcard to the database, and return when the save is complete.
153
186
  #
154
- # card = Nokogiri::XML('<vCard>...</vCard>').root
155
- # storage.save_vcard('alice@wonderland.lit', card)
156
- # puts 'saved'
187
+ # jid - The String or JID of the user, possibly nil. This may be either a
188
+ # bare JID or full JID. Implementations of this method must convert
189
+ # the JID to a bare JID before saving the vcard.
190
+ # card - The vcard's Nokogiri::XML::Node.
191
+ #
192
+ # Examples
193
+ #
194
+ # card = Nokogiri::XML('<vCard>...</vCard>').root
195
+ # storage.save_vcard('alice@wonderland.lit', card)
196
+ # puts 'saved'
197
+ #
198
+ # Returns nothing.
157
199
  def save_vcard(jid, card)
158
200
  raise 'subclass must implement'
159
201
  end
160
202
 
161
- # Return the Nokogiri::XML::Node for the XML fragment stored for this JID.
162
- # Return nil if the fragment could not be found. JID may be +nil+, a
163
- # +String+, or a +Vines::JID+ object. It may be a bare JID or a full JID.
164
- # Implementations of this method must convert the JID to a bare JID before
165
- # searching for the fragment in the database.
166
- #
167
- # Private XML storage uniquely identifies fragments by JID, root element name,
203
+ # Find the private XML fragment previously stored by the user. Private
204
+ # XML storage uniquely identifies fragments by JID, root element name,
168
205
  # and root element namespace.
169
206
  #
170
- # root = Nokogiri::XML('<custom xmlns="urn:custom:ns"/>').root
171
- # fragment = storage.find_fragment('alice@wonderland.lit', root)
172
- # puts fragment.nil?
207
+ # jid - The String or JID of the user, possibly nil. This may be either a
208
+ # bare JID or full JID. Implementations of this method must convert
209
+ # the JID to a bare JID before searching for the fragment in the database.
210
+ # node - The XML::Node that uniquely identifies the fragment by element
211
+ # name and namespace.
212
+ #
213
+ # Examples
214
+ #
215
+ # root = Nokogiri::XML('<custom xmlns="urn:custom:ns"/>').root
216
+ # fragment = storage.find_fragment('alice@wonderland.lit', root)
217
+ # puts fragment.nil?
218
+ #
219
+ # Returns the fragment's Nokogiri::XML::Node or nil if not found.
173
220
  def find_fragment(jid, node)
174
221
  raise 'subclass must implement'
175
222
  end
176
223
 
177
- # Save the XML fragment to the database and return when the save is complete.
178
- # JID may be a +String+ or a +Vines::JID+ object. It may be a bare JID or a
179
- # full JID. Implementations of this method must convert the JID to a bare
180
- # JID before saving the fragment. Fragment is a +Nokogiri::XML::Node+ object.
224
+ # Save the XML fragment to the database, and return when the save is complete.
225
+ #
226
+ # jid - The String or JID of the user, possibly nil. This may be
227
+ # either a bare JID or full JID. Implementations of this method
228
+ # must convert the JID to a bare JID before searching for the
229
+ # fragment.
230
+ # fragment - The XML::Node to save.
231
+ #
232
+ # Examples
181
233
  #
182
- # fragment = Nokogiri::XML('<custom xmlns="urn:custom:ns">some data</custom>').root
183
- # storage.save_fragment('alice@wonderland.lit', fragment)
184
- # puts 'saved'
234
+ # fragment = Nokogiri::XML('<custom xmlns="urn:custom:ns">some data</custom>').root
235
+ # storage.save_fragment('alice@wonderland.lit', fragment)
236
+ # puts 'saved'
237
+ #
238
+ # Returns nothing.
185
239
  def save_fragment(jid, fragment)
186
240
  raise 'subclass must implement'
187
241
  end
188
242
 
189
243
  private
190
244
 
191
- # Return true if any of the arguments are nil or empty strings.
192
- # For example:
193
- # username, password = 'alice@wonderland.lit', ''
194
- # empty?(username, password) #=> true
245
+ # Determine if any of the arguments are nil or empty strings.
246
+ #
247
+ # Examples
248
+ #
249
+ # username, password = 'alice@wonderland.lit', ''
250
+ # empty?(username, password) #=> true
251
+ #
252
+ # Returns true if any of the arguments are nil or empty strings.
195
253
  def empty?(*args)
196
254
  args.flatten.any? {|arg| (arg || '').strip.empty? }
197
255
  end
198
256
 
199
- # Return a +proc+ suitable for running on the +EM.defer+ thread pool that traps
200
- # and logs any errors thrown by the provided block.
257
+ # Create a proc suitable for running on the EM.defer thread pool, that
258
+ # traps and logs any errors thrown by the provided block.
259
+ #
260
+ # block - The block to wrap in error handling.
261
+ #
262
+ # Examples
263
+ #
264
+ # op = operation { do_something_on_thread_pool() }
265
+ # EM.defer(op)
266
+ #
267
+ # Returns a Proc.
201
268
  def operation
202
269
  proc do
203
270
  begin
@@ -209,10 +276,15 @@ module Vines
209
276
  end
210
277
  end
211
278
 
212
- # Return a +Vines::User+ object if we are able to bind to the LDAP server
213
- # using the username and password. Return +nil+ if authentication failed. If
279
+ # Bind to the LDAP server using the provided username and password. If
214
280
  # authentication succeeds, but the user is not yet stored in our database,
215
281
  # save the user to the database.
282
+ #
283
+ # username - The String JID to authenticate.
284
+ # password - The String password the user provided.
285
+ # block - The block that receives the authenticated User or nil.
286
+ #
287
+ # Returns the authenticated User or nil if authentication failed.
216
288
  def authenticate_with_ldap(username, password, &block)
217
289
  op = operation { ldap.authenticate(username, password) }
218
290
  cb = proc {|user| save_ldap_user(user, &block) }
@@ -220,9 +292,14 @@ module Vines
220
292
  end
221
293
  fiber :authenticate_with_ldap
222
294
 
223
- # Save missing users to the storage database after they're authenticated with
224
- # LDAP. This allows admins to define users once in LDAP and have them sync
225
- # to the chat database the first time they successfully sign in.
295
+ # Save missing users to the storage database after they're authenticated
296
+ # with LDAP. This allows admins to define users once in LDAP and have them
297
+ # sync to the chat database the first time they successfully sign in.
298
+ #
299
+ # user - The User to persist, possibly nil.
300
+ # block - The block that receives the saved User, possibly nil.
301
+ #
302
+ # Returns nothing.
226
303
  def save_ldap_user(user, &block)
227
304
  Fiber.new do
228
305
  if user.nil?