statebus 7.0.3 → 7.0.4

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.
package/servers.js CHANGED
@@ -37,7 +37,7 @@ function set_options (bus, options) {
37
37
  }
38
38
  }
39
39
 
40
- function import_server (bus, options)
40
+ function import_server (bus, make_statebus, options)
41
41
  { var extra_methods = {
42
42
 
43
43
  serve: function serve (options) {
@@ -76,23 +76,6 @@ function import_server (bus, options)
76
76
  try { bus.http.use(require('compression')())
77
77
  console.log('Enabled http compression!') } catch (e) {}
78
78
 
79
-
80
- // Initialize new clients with an id. We put the client id on
81
- // req.client, and also in a cookie for the browser to see.
82
- if (bus.options.client)
83
- bus.express.use(function (req, res, next) {
84
- req.client = require('cookie').parse(req.headers.cookie || '').client
85
- if (!req.client) {
86
- req.client = (Math.random().toString(36).substring(2)
87
- + Math.random().toString(36).substring(2)
88
- + Math.random().toString(36).substring(2))
89
-
90
- res.setHeader('Set-Cookie', 'client=' + req.client
91
- + '; Expires=21 Oct 2025 00:0:00 GMT;')
92
- }
93
- next()
94
- })
95
-
96
79
  // Initialize file sync
97
80
  ; (bus.options.sync_files || []).forEach( x => {
98
81
  if (require('fs').existsSync(x.fs_path || x.state_path))
@@ -165,20 +148,34 @@ function import_server (bus, options)
165
148
  || req.method === 'OPTIONS')
166
149
  free_the_cors(req, res)
167
150
 
151
+ // Initialize new clients with an id. We put the client id on
152
+ // req.client, and also in a cookie for the browser to see.
153
+ req.peer = //req.headers.peer ||
154
+ require('cookie').parse(req.headers.cookie || '').peer
155
+ if (!req.peer) {
156
+ req.peer = (Math.random().toString(36).substring(2)
157
+ + Math.random().toString(36).substring(2)
158
+ + Math.random().toString(36).substring(2))
159
+
160
+ res.setHeader('Set-Cookie', 'peer=' + req.peer
161
+ + '; Expires=21 Oct 2055 00:0:00 GMT;')
162
+ }
163
+
164
+ // Handle the GET or PUT request!
168
165
  if (req.method === 'GET') {
169
166
 
170
167
  // Make a temporary client bus
171
168
  var cbus = bus.bus_for_http_client(req, res)
172
169
  var cb
173
- function end_it_all () {
170
+ function end_subscription () {
174
171
  cbus.forget(key, cb)
175
- cbus.delete_bus()
176
172
  res.end()
173
+ cbus.http_unsubscribe(req)
177
174
  }
178
175
 
179
176
  braidify(req, res)
180
177
  if (req.subscribe)
181
- res.startSubscription({ onClose: end_it_all })
178
+ res.startSubscription({ onClose: end_subscription })
182
179
  else
183
180
  res.statusCode = 200
184
181
 
@@ -188,7 +185,9 @@ function import_server (bus, options)
188
185
  // Do the get
189
186
  // cbus.honk = 'statelog'
190
187
  var key = req.path.substr(1)
188
+ // console.log('http_in: doing cbus.get(', key, ')')
191
189
  cbus.get(key, cb = (o) => {
190
+ // console.trace('http_in: Sending update of', key)
192
191
  var body = to_http_body(o)
193
192
 
194
193
  // If we're braiding, send via subscription
@@ -200,14 +199,11 @@ function import_server (bus, options)
200
199
 
201
200
  // And shut down the connection if there's no subscription
202
201
  if (!req.subscribe)
203
- end_it_all()
202
+ end_subscription()
204
203
  })
205
204
  }
206
205
 
207
206
  else if (req.method === 'PUT') {
208
- // Make a temporary client bus
209
- var cbus = bus.bus_for_http_client(req, res)
210
-
211
207
  // Maybe the new state is expressed in patches
212
208
  if (req.headers.patches) {
213
209
  console.error("We don't actually handle PUT patches yet")
@@ -234,9 +230,14 @@ function import_server (bus, options)
234
230
  res.end()
235
231
  return
236
232
  }
237
- cbus.set(obj)
233
+
234
+ // Make a temporary client bus
235
+ var cbus = bus.bus_for_http_client(req, res)
236
+ cbus.set(obj) // And use it
237
+
238
238
  res.statusCode = 200
239
239
  res.end()
240
+ cbus.http_unsubscribe(req)
240
241
  })
241
242
  }
242
243
  }
@@ -246,16 +247,39 @@ function import_server (bus, options)
246
247
 
247
248
  bus_for_http_client: function (req, res) {
248
249
  var bus = this
249
- if (!bus.bus_for_http_client.counter)
250
+ if (!bus.bus_for_http_client.counter) {
250
251
  bus.bus_for_http_client.counter = 0
251
- var cbus = require('./statebus')()
252
+ bus.bus_for_http_client.busses = {}
253
+ }
254
+
255
+ // If this client has a peer ID, and we've got a bus for that peer,
256
+ // then re-use it!
257
+ if (req.peer && bus.bus_for_http_client.busses[req.peer]) {
258
+ var cbus = bus.bus_for_http_client.busses[req.peer]
259
+ cbus.client_ip = req.connection.remoteAddress
260
+ cbus.num_subscriptions++
261
+ return cbus
262
+ }
263
+
264
+ // Otherwise, create a new bus
265
+ var cbus = make_statebus()
252
266
  cbus.label = 'client_http' + bus.bus_for_http_client.counter++
253
267
  cbus.master = bus
268
+ cbus.num_subscriptions = 1
269
+ cbus.http_unsubscribe = (req) => {
270
+ cbus.num_subscriptions--
271
+ if (cbus.num_subscriptions === 0) {
272
+ // log('Last subscription! Killing client bus!')
273
+ cbus.delete_bus()
274
+ delete bus.bus_for_http_client.busses[req.peer]
275
+ }
276
+ }
277
+ bus.bus_for_http_client.busses[req.peer] = cbus
254
278
 
255
- // Log in as the client
279
+ // And log into it as the client
256
280
  cbus.serves_auth({remoteAddress: req.connection.remoteAddress}, bus)
257
281
  bus.options.client(cbus)
258
- cbus.set({key: 'current_user', client: req.client})
282
+ cbus.set({key: 'current_user', val: {client: req.peer}})
259
283
  return cbus
260
284
  },
261
285
 
@@ -358,7 +382,7 @@ function import_server (bus, options)
358
382
  // var client_busses = {} // XXX work in progress
359
383
  var log = master.log
360
384
  if (client_bus_func) {
361
- master.set({key: 'connections'}) // Clean out old sessions
385
+ master.set({key: 'connections', val: {}}) // Clean out old sessions
362
386
  var connections = master.get('connections')
363
387
  }
364
388
  var s = require('sockjs').createServer({
@@ -376,11 +400,11 @@ function import_server (bus, options)
376
400
  // - When disconnecting, decrement the number, and if it gets
377
401
  // to zero, delete the client bus.
378
402
 
379
- connections[conn.id] = {client: conn.id, // client is deprecated
380
- id: conn.id}
403
+ connections.val[conn.id] = {client: conn.id, // client is deprecated
404
+ id: conn.id}
381
405
  master.set(connections)
382
406
 
383
- var client = require('./statebus')()
407
+ var client = make_statebus()
384
408
  client.label = 'client' + client_num++
385
409
  master.label = master.label || 'master'
386
410
  client.master = master
@@ -489,7 +513,7 @@ function import_server (bus, options)
489
513
  for (var key in our_gets_in)
490
514
  client.forget(key, sockjs_pubber)
491
515
  if (client_bus_func) {
492
- delete connections[conn.id]; master.set(connections)
516
+ delete connections.val[conn.id]; master.set(connections)
493
517
  master.delete('connection/' + conn.id)
494
518
  client.delete_bus()
495
519
  }
@@ -537,12 +561,12 @@ function import_server (bus, options)
537
561
  client('connections').to_set = function noop (t) {t.abort()}
538
562
  client('connections').to_get = function () {
539
563
  var result = []
540
- var conns = master.get('connections')
564
+ var conns = master.get('connections').val
541
565
  for (var connid in conns)
542
566
  if (connid !== 'key')
543
567
  result.push(client.get('connection/' + connid))
544
568
 
545
- return {all: result}
569
+ return {val: result}
546
570
  }
547
571
  }
548
572
  })
@@ -558,13 +582,6 @@ function import_server (bus, options)
558
582
  WebSocket = require('websocket').w3cwebsocket
559
583
  return new WebSocket(url + '/' + bus.options.websocket_path + '/websocket')
560
584
  },
561
- client_creds: function client_creds (server_url) {
562
- // Right now the server just creates a different random id each time
563
- // it connects.
564
- return {clientid: (Math.random().toString(36).substring(2)
565
- + Math.random().toString(36).substring(2)
566
- + Math.random().toString(36).substring(2))}
567
- },
568
585
 
569
586
  file_store: (function () {
570
587
  // Make a database
@@ -1310,23 +1327,24 @@ function import_server (bus, options)
1310
1327
  }
1311
1328
 
1312
1329
  function user_addy (u) {
1330
+ console.error('fix this! not ported to v7 links')
1313
1331
  return u.name + '@' + opts.domain
1314
1332
  }
1315
1333
  function current_addy () {
1316
1334
  var c = client.get('current_user')
1317
- return c.logged_in ? user_addy(c.user) : 'public'
1335
+ console.error('fix this! not ported to v7 links')
1336
+ return c.val.logged_in ? user_addy(c.val.user) : 'public'
1318
1337
  }
1319
1338
  function is_author (post) {
1320
1339
  var from = post._.from
1321
- var c = client.get('current_user')
1322
1340
  return from.includes(current_addy())
1323
1341
  }
1324
1342
  function can_see (post) {
1325
- var c = client.get('current_user')
1343
+ console.error('fix this! not ported to v7')
1326
1344
  var allowed = post._.to.concat(post._.cc).concat(post._.from)
1327
1345
  return allowed.includes(current_addy()) || allowed.includes('public')
1328
1346
  }
1329
- var drtbus = master//require('./statebus')()
1347
+ var drtbus = master//make_statebus()
1330
1348
  function dirty (key) { drtbus.set({key: 'dirty-'+key, n: Math.random()}) }
1331
1349
  function watch_for_dirt (key) { drtbus.get('dirty-'+key) }
1332
1350
 
@@ -1337,7 +1355,6 @@ function import_server (bus, options)
1337
1355
  // Define state on client
1338
1356
  client('posts*').to_get = (k, rest) => {
1339
1357
  var args = bus.parse(rest.substr(1))
1340
- var c = client.get('current_user')
1341
1358
  args.for = current_addy()
1342
1359
  var e = master.get('posts_for/' + JSON.stringify(args))
1343
1360
  return {_: master.clone(e._).map(x=>client.get(x.key))}
@@ -1415,7 +1432,7 @@ function import_server (bus, options)
1415
1432
  }
1416
1433
 
1417
1434
  client('friends').to_get = t => {
1418
- return {_: (master.get('users').all||[])
1435
+ return {_: (master.get('users').val||[])
1419
1436
  .map(u=>client.get('email/' + user_addy(u)))
1420
1437
  .concat([client.get('email/public')])
1421
1438
  }
@@ -1643,42 +1660,55 @@ function import_server (bus, options)
1643
1660
 
1644
1661
  serves_auth: function serves_auth (conn, master) {
1645
1662
  var client = this // to keep me straight while programming
1646
-
1647
- function logout (key) {
1663
+ function logout (user_key) {
1648
1664
  var clients = master.get('logged_in_clients')
1649
- for (var k in clients)
1650
- if (k !== 'key')
1651
- if (Object.keys(clients[k]).length === 0) {
1652
- client.log('Found a deleted user. Removing.', k, clients[k])
1653
- delete clients[k]
1654
- master.set(clients)
1655
- } else if (clients[k].key === key) {
1656
- client.log('Logging out!', k, clients[k])
1657
- delete clients[k]
1658
- master.set(clients)
1659
- }
1665
+ clients.val = clients.val || {}
1666
+
1667
+ for (var k in clients.val) {
1668
+ // We used to encounter stray deleted accounts that hadn't
1669
+ // gotten properly garbage collected, and I added some code
1670
+ // here to remove them.
1671
+ //
1672
+ // However, it was a bit kludgy, and I'm rewriting the code
1673
+ // now, and not sure if this problem still occurs. Will watch
1674
+ // and see. Meanwhile, I'm commenting the code out.
1675
+ //
1676
+ // if (Object.keys(clients.val[k]).length === 0) {
1677
+ // client.log('Found a deleted user. Removing.', k, clients.val[k])
1678
+ // delete clients.val[k]
1679
+ // master.set(clients)
1680
+ // }
1681
+ if (clients.val[k].link === user_key) {
1682
+ client.log('Logging out!', k, clients.val[k])
1683
+ delete clients.val[k]
1684
+ master.set(clients)
1685
+ }
1686
+ }
1660
1687
  }
1661
1688
 
1662
1689
  // Initialize master
1663
1690
  if (!master.auth_initialized) {
1691
+
1692
+ // A hash for fast lookup of a user's password
1664
1693
  master('users/passwords').to_get = function (k) {
1694
+ // We compute it from the 'users' state
1665
1695
  master.log('users/passwords.to_get: Computing!')
1666
- var result = {key: 'users/passwords'}
1696
+ var result = {key: 'users/passwords', val: {}}
1667
1697
  var users = master.get('users')
1668
- users.all = users.all || []
1669
- for (var i=0; i<users.all.length; i++) {
1670
- var u = master.get(users.all[i])
1671
- if (!(u.login || u.name)) {
1672
- console.error('upass: this user has bogus name/login', u.key, u.name, u.login)
1698
+ users.val = users.val || []
1699
+ for (var i=0; i<users.val.length; i++) {
1700
+ var u = master.get(users.val[i].link)
1701
+ if (!(u.val.login || u.val.name)) {
1702
+ console.error('upass: this user has bogus name/login', u.key, u.val.name, u.val.login)
1673
1703
  continue
1674
1704
  }
1675
- var login = (u.login || u.name).toLowerCase()
1705
+ var login = (u.val.login || u.val.name).toLowerCase()
1676
1706
  console.assert(login, 'Missing login for user', u)
1677
- if (result.hasOwnProperty(login)) {
1707
+ if (result.val.hasOwnProperty(login)) {
1678
1708
  console.error("upass: this user's name is bogus, dude.", u.key)
1679
1709
  continue
1680
1710
  }
1681
- result[login] = {user: u.key, pass: u.pass}
1711
+ result.val[login] = {user: u.key, pass: u.val.pass}
1682
1712
  }
1683
1713
  return result
1684
1714
  }
@@ -1689,26 +1719,25 @@ function import_server (bus, options)
1689
1719
  master.log('Deleteinggg!!!', key)
1690
1720
  // Remove from users.all
1691
1721
  var users = master.get('users')
1692
- users.all = users.all.filter(u => u.key && u.key !== key)
1722
+ users.val = users.val.filter(u => u.link && u.link !== key)
1693
1723
  master.set(users)
1694
1724
 
1695
1725
  // Log out
1696
1726
  master.log('Logging out', key)
1697
1727
  logout(key)
1698
1728
 
1699
- // Remove connection
1700
- master.log('Removing connections for', key)
1701
- var conns = master.get('connections')
1702
- for (var k in conns) {
1703
- console.log('Trying key', k)
1704
- if (k !== 'key')
1705
- if (conns[k].user && !conns[k].user.key) {
1706
- console.log('Cleaning keyless user', conss[k].user)
1707
- delete conns[k].user
1708
- master.set(conns)
1709
- continue
1710
- }
1711
- }
1729
+ // // Remove connection
1730
+ // master.log('Removing connections for', key)
1731
+ // var conns = master.get('connections')
1732
+ // for (var k in conns.val) {
1733
+ // console.log('Trying key', k)
1734
+ // if (conns.val[k].user && !conns.val[k].user.key) {
1735
+ // console.log('Cleaning keyless user', conss.val[k].user)
1736
+ // delete conns.val[k].user
1737
+ // master.set(conns)
1738
+ // continue
1739
+ // }
1740
+ // }
1712
1741
 
1713
1742
  master.log('Dirtying users/passwords for', key)
1714
1743
  master.dirty('users/passwords')
@@ -1720,9 +1749,9 @@ function import_server (bus, options)
1720
1749
 
1721
1750
  // Authentication functions
1722
1751
  function authenticate (login, pass) {
1723
- var userpass = master.get('users/passwords')[login.toLowerCase()]
1752
+ var userpass = master.get('users/passwords').val[login.toLowerCase()]
1724
1753
  master.log('authenticate: we see', {
1725
- passwords: master.get('users/passwords'),
1754
+ passwords: master.get('users/passwords').val,
1726
1755
  hash_to_match: userpass && userpass.pass,
1727
1756
  password_guess: pass
1728
1757
  })
@@ -1733,7 +1762,7 @@ function import_server (bus, options)
1733
1762
 
1734
1763
  // console.log('comparing passwords', pass, userpass.pass)
1735
1764
  if (require('bcrypt-nodejs').compareSync(pass, userpass.pass))
1736
- return master.get(userpass.user)
1765
+ return userpass.user
1737
1766
  }
1738
1767
  function create_account (params) {
1739
1768
  if (typeof (params.login || params.name) !== 'string')
@@ -1746,7 +1775,7 @@ function import_server (bus, options)
1746
1775
  throw 'invalid name, login, pass, or email'
1747
1776
 
1748
1777
  var passes = master.get('users/passwords')
1749
- if (passes.hasOwnProperty(login))
1778
+ if (passes.val.hasOwnProperty(login))
1750
1779
  throw 'there is already a user with that login or name'
1751
1780
 
1752
1781
  // Hash password
@@ -1766,60 +1795,81 @@ function import_server (bus, options)
1766
1795
  key = 'user/' + Math.random().toString(36).substring(7,13)
1767
1796
 
1768
1797
  // Make account object
1769
- var new_account = {key: key,
1770
- name: params.name,
1771
- login: params.login,
1772
- pass: params.pass,
1773
- email: params.email }
1774
- for (var k in new_account) if (!new_account[k]) delete new_account[k]
1798
+ var new_account = {
1799
+ key: key, val: {
1800
+ name: params.name,
1801
+ login: params.login,
1802
+ pass: params.pass,
1803
+ email: params.email
1804
+ }
1805
+ }
1806
+ for (var k in new_account.val) // Clean out falsy fields
1807
+ if (!new_account.val[k])
1808
+ delete new_account.val[k]
1775
1809
  master.set(new_account)
1776
1810
 
1777
1811
  var users = master.get('users')
1778
- users.all = users.all || []
1779
- users.all.push(new_account)
1780
- passes[login] = {user: new_account.key, pass: new_account.pass}
1812
+ users.val = users.val || []
1813
+ users.val.push({link: new_account.key})
1814
+ passes.val[login] = {user: new_account.key, pass: new_account.val.pass}
1781
1815
  master.set(users)
1782
1816
  master.set(passes)
1783
1817
  }
1784
1818
 
1785
1819
  // Current User
1786
1820
  client('current_user').to_get = function (k) {
1787
- client.log('* getting: current_user')
1788
- if (!conn.client) return
1789
- var u = master.get('logged_in_clients')[conn.client]
1790
- u = u && user_obj(u.key, true)
1791
- return {user: u || null, logged_in: !!u}
1821
+ if (!conn.client)
1822
+ return {val: {error: 'no client'}}
1823
+
1824
+ var u = (master.get('logged_in_clients').val || {})[conn.client]
1825
+ var result = {val: {user: u || null, logged_in: !!u}}
1826
+ return result
1792
1827
  }
1793
1828
 
1794
1829
  client('current_user').to_set = function (o, t) {
1830
+ var val = o.val
1831
+ if (typeof val !== 'object') {
1832
+ console.error('current_user: set to something not an object:', val)
1833
+ t.abort()
1834
+ return
1835
+ }
1795
1836
  function error (msg) {
1837
+ console.error(msg)
1796
1838
  client.set.abort(o)
1797
1839
  var c = client.get('current_user')
1798
- c.error = msg
1840
+ c.val.error = msg
1799
1841
  client.set.fire(c)
1800
1842
  }
1801
1843
 
1802
1844
  client.log('* saving: current_user!')
1803
- if (o.client && !conn.client) {
1845
+ if (val.client && !conn.client) {
1804
1846
  // Set the client
1805
- conn.client = o.client
1806
- client.client_id = o.client
1847
+ //
1848
+ // Note: Should this code be moved upstream, when creating a
1849
+ // client bus?
1850
+ conn.client = val.client
1851
+ client.client_id = val.client
1807
1852
  client.client_ip = conn.remoteAddress
1808
1853
 
1809
- if (conn.id) {
1810
- var connections = master.get('connections')
1811
- connections[conn.id].user = master.get('logged_in_clients')[conn.client]
1812
- master.set(connections)
1813
- }
1854
+ // if (conn.id) {
1855
+ // var connections = master.get('connections')
1856
+ // var logged_in_user =
1857
+ // (master.get('logged_in_clients').val || {})[conn.client]
1858
+
1859
+ // if (logged_in_user) {
1860
+ // connections.val[conn.id].user = {link: user.link}
1861
+ // master.set(connections)
1862
+ // }
1863
+ // }
1814
1864
  }
1815
1865
  else {
1816
- if (o.create_account) {
1866
+ if (val.create_account) {
1817
1867
  client.log('current_user: creating account')
1818
1868
  try {
1819
- create_account(o.create_account)
1869
+ create_account(val.create_account)
1820
1870
  client.log('Success creating account!')
1821
1871
  var cu = client.get('current_user')
1822
- cu.create_account = null
1872
+ cu.val.create_account = null
1823
1873
  client.set.fire(cu)
1824
1874
  } catch (e) {
1825
1875
  error('Cannot create that account because ' + e)
@@ -1827,29 +1877,29 @@ function import_server (bus, options)
1827
1877
  }
1828
1878
  }
1829
1879
 
1830
- if (o.login_as && conn.id) {
1880
+ if (val.login_as) {
1831
1881
  // Then client is trying to log in
1832
- client.log('current_user: trying to log in')
1833
- var creds = o.login_as
1882
+ var creds = val.login_as
1834
1883
  var login = creds.login || creds.name
1835
1884
  if (login && creds.pass) {
1836
1885
  // With a username and password
1837
- var u = authenticate(login, creds.pass)
1886
+ var user_key = authenticate(login, creds.pass)
1838
1887
 
1839
- client.log('auth said:', u)
1840
- if (u) {
1888
+ client.log('auth said:', user_key)
1889
+ if (user_key) {
1841
1890
  // Success!
1842
1891
  // Associate this user with this session
1843
1892
  // user.log('Logging the user in!', u)
1844
1893
 
1845
1894
  var clients = master.get('logged_in_clients')
1846
- var connections = master.get('connections')
1895
+ // var connections = master.get('connections')
1847
1896
 
1848
- clients[conn.client] = u
1849
- connections[conn.id].user = u
1897
+ clients.val = clients.val || {}
1898
+ clients.val[conn.client] = {link: user_key}
1899
+ // connections.val[conn.id].user = {link: user_key}
1850
1900
 
1851
1901
  master.set(clients)
1852
- master.set(connections)
1902
+ // master.set(connections)
1853
1903
 
1854
1904
  client.log('current_user: success logging in!')
1855
1905
  }
@@ -1864,16 +1914,17 @@ function import_server (bus, options)
1864
1914
  }
1865
1915
  }
1866
1916
 
1867
- else if (o.logout && conn.id) {
1917
+ else if (val.logout) {
1868
1918
  client.log('current_user: logging out')
1869
1919
  var clients = master.get('logged_in_clients')
1870
- var connections = master.get('connections')
1920
+ // var connections = master.get('connections')
1871
1921
 
1872
- delete clients[conn.client]
1873
- connections[conn.id].user = null
1922
+ clients.val = clients.val || {}
1923
+ delete clients.val[conn.client]
1924
+ // connections.val[conn.id].user = null
1874
1925
 
1875
1926
  master.set(clients)
1876
- master.set(connections)
1927
+ // master.set(connections)
1877
1928
  }
1878
1929
  }
1879
1930
 
@@ -1891,11 +1942,13 @@ function import_server (bus, options)
1891
1942
  var user_key = o.key.match(/^user\/([^\/]+)/)
1892
1943
  user_key = user_key && ('user/' + user_key[1])
1893
1944
 
1894
- // Only the current user can touch themself.
1895
- if (!c.logged_in || c.user.key !== user_key) {
1896
- client.log('Only the current user can touch themself.',
1897
- {logged_in: c.logged_in, as: c.user && c.user.key,
1898
- touching: user_key})
1945
+ // Only the current user can touch himself.
1946
+ if (!c.val.logged_in || c.val.user.link !== user_key) {
1947
+ client.log('Only the current user can touch himself.', {
1948
+ logged_in: c.val.logged_in,
1949
+ as: c.val.user && c.val.user.link,
1950
+ touching: user_key
1951
+ })
1899
1952
  client.set.abort(o)
1900
1953
  return
1901
1954
  }
@@ -1911,9 +1964,14 @@ function import_server (bus, options)
1911
1964
  console.assert(o.key.match(/^user\/[^\/]+$/))
1912
1965
 
1913
1966
  // Validate types
1914
- if (!client.validate(o, {key: 'string', '?login': 'string', '?name': 'string',
1915
- '?pass': 'string', '?email': 'string', /*'?pic': 'string',*/
1916
- '*':'*'})) {
1967
+ if (!client.validate(o, {key: 'string',
1968
+ val: {
1969
+ '?login': 'string',
1970
+ '?name': 'string',
1971
+ '?pass': 'string',
1972
+ '?email': 'string',
1973
+ /*'?pic': 'string',*/
1974
+ '*':'*'}})) {
1917
1975
  client.log('This user change fails validation.')
1918
1976
  client.set.abort(o)
1919
1977
  return
@@ -1924,7 +1982,7 @@ function import_server (bus, options)
1924
1982
  // • That resulting login must be unique across all users
1925
1983
 
1926
1984
  // There must be at least a login or a name
1927
- var login = o.login || o.name
1985
+ var login = o.val.login || o.val.name
1928
1986
  if (!login) {
1929
1987
  client.log('User must have a login or a name')
1930
1988
  client.set.abort(o)
@@ -1935,26 +1993,26 @@ function import_server (bus, options)
1935
1993
  var userpass = master.get('users/passwords')
1936
1994
 
1937
1995
  // Validate that the login/name is not changed to something clobberish
1938
- var old_login = u.login || u.name
1996
+ var old_login = u.val.login || u.val.name
1939
1997
  if (old_login.toLowerCase() !== login.toLowerCase()
1940
1998
  && userpass.hasOwnProperty(login)) {
1941
1999
  client.log('The login', login, 'is already taken. Aborting.')
1942
2000
  client.set.abort(o) // Abort
1943
2001
 
1944
2002
  o = client.get(o.key) // Add error message
1945
- o.error = 'The login "' + login + '" is already taken'
2003
+ o.val.error = 'The login "' + login + '" is already taken'
1946
2004
  client.set.fire(o)
1947
2005
 
1948
2006
  return // And exit
1949
2007
  }
1950
2008
 
1951
2009
  // Now we can update login and name
1952
- u.login = o.login
1953
- u.name = o.name
2010
+ u.val.login = o.val.login
2011
+ u.val.name = o.val.name
1954
2012
 
1955
2013
  // Hash password
1956
- o.pass = o.pass && require('bcrypt-nodejs').hashSync(o.pass)
1957
- u.pass = o.pass || u.pass
2014
+ o.val.pass = o.val.pass && require('bcrypt-nodejs').hashSync(o.val.pass)
2015
+ u.val.pass = o.val.pass || u.val.pass
1958
2016
 
1959
2017
  // // Users can have pictures (remove this soon)
1960
2018
  // // Bug: if user changes name, this picture's url doesn't change.
@@ -1973,24 +2031,24 @@ function import_server (bus, options)
1973
2031
 
1974
2032
  // For anything else, go ahead and add it to the user object
1975
2033
  var protected = {key:1, name:1, /*pic:1,*/ pass:1}
1976
- for (var k in o)
2034
+ for (var k in o.val)
1977
2035
  if (!protected.hasOwnProperty(k))
1978
- u[k] = o[k]
1979
- for (var k in u)
1980
- if (!protected.hasOwnProperty(k) && !o.hasOwnProperty(k))
1981
- delete u[k]
2036
+ u.val[k] = o.val[k]
2037
+ for (var k in u.val)
2038
+ if (!protected.hasOwnProperty(k) && !o.val.hasOwnProperty(k))
2039
+ delete u.val[k]
1982
2040
 
1983
2041
  master.set(u)
1984
2042
  }
1985
2043
  client('user/*').to_get = function user_getter (k) {
1986
2044
  var c = client.get('current_user')
1987
- client.log('* getting:', k, 'as', c.user)
2045
+ client.log('* getting:', k, 'as', c.val.user)
1988
2046
 
1989
2047
  // Users have closet space at /user/<name>/*
1990
2048
  if (k.match(closet_space_key)) {
1991
2049
  var obj_user = k.match(closet_space_key)[1]
1992
2050
  if (k.match(private_closet_space_key)
1993
- && (!c.user || obj_user !== c.user.key)) {
2051
+ && (!c.val.user || obj_user !== c.val.user.link)) {
1994
2052
  client.log('hiding private closet data')
1995
2053
  return {}
1996
2054
  }
@@ -1999,16 +2057,17 @@ function import_server (bus, options)
1999
2057
  }
2000
2058
 
2001
2059
  // Otherwise return the actual user
2002
- return user_obj(k, c.logged_in && c.user.key === k)
2060
+ return user_obj(k, c.val.logged_in && c.val.user.link === k)
2003
2061
  }
2004
2062
  client('user/*').to_delete = function () {}
2005
2063
  function user_obj (k, logged_in) {
2006
2064
  var o = master.clone(master.get(k))
2007
2065
  if (k.match(/^user\/([^\/]+)\/private\/(.*)$/))
2008
2066
  return logged_in ? o : {key: k}
2009
-
2010
- delete o.pass
2011
- if (!logged_in) {delete o.email; delete o.login}
2067
+
2068
+ o.val = o.val || {}
2069
+ delete o.val.pass
2070
+ if (!logged_in) {delete o.val.email; delete o.val.login}
2012
2071
  return o
2013
2072
  }
2014
2073
 
@@ -2027,14 +2086,14 @@ function import_server (bus, options)
2027
2086
  var was_logged_in = undefined
2028
2087
 
2029
2088
  function client_prefix (current_user) {
2030
- return 'client/' + (current_user.logged_in
2031
- ? current_user.user.key.substr('user/'.length)
2089
+ return 'client/' + (current_user.val.logged_in
2090
+ ? current_user.val.user.link.substr('user/'.length)
2032
2091
  : client.client_id) + '/'
2033
2092
  }
2034
2093
 
2035
- function copy_client_to_user(client, user) {
2094
+ function copy_client_to_user(client, user_key) {
2036
2095
  var old_prefix = 'client/' + client.client_id
2037
- var new_prefix = 'client/' + user.key.substr('user/'.length)
2096
+ var new_prefix = 'client/' + user_key.substr('user/'.length)
2038
2097
 
2039
2098
  var keys = client.master.get('persisted_keys/' + client.client_id)
2040
2099
  if (!keys.val) return
@@ -2080,10 +2139,10 @@ function import_server (bus, options)
2080
2139
  client(_=>{
2081
2140
  var c = client.get('current_user')
2082
2141
  if (client.loading()) return
2083
- if (was_logged_in == false && c.logged_in)
2142
+ if (was_logged_in == false && c.val.logged_in)
2084
2143
  // User just logged in! Let's copy his stuff over
2085
- copy_client_to_user(client, c.user)
2086
- was_logged_in = c.logged_in
2144
+ copy_client_to_user(client, c.val.user.link)
2145
+ was_logged_in = c.val.logged_in
2087
2146
  })
2088
2147
 
2089
2148
  client(prefix_to_sync).to_get = function (key) {
@@ -2141,7 +2200,7 @@ function import_server (bus, options)
2141
2200
  }
2142
2201
 
2143
2202
  function client_persisted_keys () {
2144
- if (client.get('current_user').logged_in) return
2203
+ if (client.get('current_user').val.logged_in) return
2145
2204
  var result = client.master.get('persisted_keys/' + client.client_id)
2146
2205
  if (result && !result.val) result.val = {}
2147
2206
  return result
@@ -2274,7 +2333,7 @@ function import_server (bus, options)
2274
2333
  // Installs a GET handler at route that gets state from a getter function
2275
2334
  // Note: Makes too many textbusses. Should re-use one.
2276
2335
  http_serve: function http_serve (route, getter) {
2277
- var textbus = require('./statebus')()
2336
+ var textbus = make_statebus()
2278
2337
  textbus.label = 'textbus'
2279
2338
  var watched = new Set()
2280
2339
  textbus('*').to_get = (filename, old) => {