Merge branch 'staging' into communities-trim

pull/1/head
yflory 5 years ago
commit 9a857ea058

@ -1,3 +1,42 @@
# L release (3.11.0)
## Goals
* major server refactor to prepare for:
* trim-history
* allow lists
## Update notes
* dropped support for retainData
* archives are on by default
* you will need a new chainpad server
## Features
* restyled corner popup
* cool new scheduler library
* operations on channels are queued
* trim-history rpc
* unified historykeeper and rpc
* more visible styles for unanswered support tickets
* hidden hashes/safe links
* new "security" tab in settings
* queue'd popups
* reconnect alert
* link to user profile in notifications
* prompt anonymous users to register when viewing a profile
* spreadsheets
* reconnecting spreadsheets
* faster spreadsheets
* don't hijack chat cursor
* friends are now "contacts"
## Bug fixes
* friend request/accept race condition
* throw errors in 'mkAsync' if no function is passed
# Kouprey release (3.10.0) # Kouprey release (3.10.0)
## Goals ## Goals

@ -2,6 +2,8 @@
const BatchRead = require("../batch-read"); const BatchRead = require("../batch-read");
const nThen = require("nthen"); const nThen = require("nthen");
const getFolderSize = require("get-folder-size"); const getFolderSize = require("get-folder-size");
const Util = require("../common-util");
var Fs = require("fs"); var Fs = require("fs");
var Admin = module.exports; var Admin = module.exports;
@ -90,9 +92,10 @@ var getDiskUsage = function (Env, cb) {
}); });
}; };
Admin.command = function (Env, Server, publicKey, data, cb) { Admin.command = function (Env, safeKey, data, cb, Server) {
var admins = Env.admins; var admins = Env.admins;
if (admins.indexOf(publicKey) === -1) { var unsafeKey = Util.unescapeKeyCharacters(safeKey);
if (admins.indexOf(unsafeKey) === -1) {
return void cb("FORBIDDEN"); return void cb("FORBIDDEN");
} }

@ -31,7 +31,7 @@ const Util = require("../common-util");
author of the block, since we assume that the block will have been author of the block, since we assume that the block will have been
encrypted with xsalsa20-poly1305 which is authenticated. encrypted with xsalsa20-poly1305 which is authenticated.
*/ */
Block.validateLoginBlock = function (Env, publicKey, signature, block, cb) { // FIXME BLOCKS var validateLoginBlock = function (Env, publicKey, signature, block, cb) { // FIXME BLOCKS
// convert the public key to a Uint8Array and validate it // convert the public key to a Uint8Array and validate it
if (typeof(publicKey) !== 'string') { return void cb('E_INVALID_KEY'); } if (typeof(publicKey) !== 'string') { return void cb('E_INVALID_KEY'); }
@ -86,13 +86,13 @@ var createLoginBlockPath = function (Env, publicKey) { // FIXME BLOCKS
return Path.join(Env.paths.block, safeKey.slice(0, 2), safeKey); return Path.join(Env.paths.block, safeKey.slice(0, 2), safeKey);
}; };
Block.writeLoginBlock = function (Env, msg, cb) { // FIXME BLOCKS Block.writeLoginBlock = function (Env, safeKey, msg, cb) { // FIXME BLOCKS
//console.log(msg); //console.log(msg);
var publicKey = msg[0]; var publicKey = msg[0];
var signature = msg[1]; var signature = msg[1];
var block = msg[2]; var block = msg[2];
Block.validateLoginBlock(Env, publicKey, signature, block, function (e, validatedBlock) { validateLoginBlock(Env, publicKey, signature, block, function (e, validatedBlock) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
if (!(validatedBlock instanceof Uint8Array)) { return void cb('E_INVALID_BLOCK'); } if (!(validatedBlock instanceof Uint8Array)) { return void cb('E_INVALID_BLOCK'); }
@ -141,12 +141,12 @@ Block.writeLoginBlock = function (Env, msg, cb) { // FIXME BLOCKS
information, we can just sign some constant and use that as proof. information, we can just sign some constant and use that as proof.
*/ */
Block.removeLoginBlock = function (Env, msg, cb) { // FIXME BLOCKS Block.removeLoginBlock = function (Env, safeKey, msg, cb) { // FIXME BLOCKS
var publicKey = msg[0]; var publicKey = msg[0];
var signature = msg[1]; var signature = msg[1];
var block = Nacl.util.decodeUTF8('DELETE_BLOCK'); // clients and the server will have to agree on this constant var block = Nacl.util.decodeUTF8('DELETE_BLOCK'); // clients and the server will have to agree on this constant
Block.validateLoginBlock(Env, publicKey, signature, block, function (e /*::, validatedBlock */) { validateLoginBlock(Env, publicKey, signature, block, function (e /*::, validatedBlock */) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
// derive the filepath // derive the filepath
var path = createLoginBlockPath(Env, publicKey); var path = createLoginBlockPath(Env, publicKey);

@ -160,7 +160,7 @@ Channel.isNewChannel = function (Env, channel, cb) {
Otherwise behaves the same as sending to a channel Otherwise behaves the same as sending to a channel
*/ */
Channel.writePrivateMessage = function (Env, args, Server, cb) { Channel.writePrivateMessage = function (Env, args, cb, Server) { // XXX odd signature
var channelId = args[0]; var channelId = args[0];
var msg = args[1]; var msg = args[1];

@ -184,5 +184,7 @@ Core.isPendingOwner = function (metadata, unsafeKey) {
return metadata.pending_owners.indexOf(unsafeKey) !== -1; return metadata.pending_owners.indexOf(unsafeKey) !== -1;
}; };
Core.haveACookie = function (Env, safeKey, cb) {
cb();
};

@ -8,10 +8,12 @@ const Core = require("./core");
const Util = require("../common-util"); const Util = require("../common-util");
const batchMetadata = BatchRead("GET_METADATA"); const batchMetadata = BatchRead("GET_METADATA");
Data.getMetadata = function (Env, channel, cb) { Data.getMetadata = function (Env, channel, cb/* , Server */) {
if (!Core.isValidId(channel)) { return void cb('INVALID_CHAN'); } if (!Core.isValidId(channel)) { return void cb('INVALID_CHAN'); }
if (channel.length !== 32) { return cb("INVALID_CHAN_LENGTH"); } if (channel.length !== 32) { return cb("INVALID_CHAN_LENGTH"); }
// XXX get metadata from the server cache if it is available
// Server isn't always passed, though...
batchMetadata(channel, cb, function (done) { batchMetadata(channel, cb, function (done) {
var ref = {}; var ref = {};
var lineHandler = Meta.createLineHandler(ref, Env.Log.error); var lineHandler = Meta.createLineHandler(ref, Env.Log.error);

@ -199,7 +199,8 @@ Pinning.removePins = function (Env, safeKey, cb) {
status: err? String(err): 'SUCCESS', status: err? String(err): 'SUCCESS',
}); });
cb(err); if (err) { return void cb(err); }
cb(void 0, 'OK');
}); });
}; };
@ -453,10 +454,10 @@ Pinning.loadChannelPins = function (Env) {
Pinning.isChannelPinned = function (Env, channel, cb) { Pinning.isChannelPinned = function (Env, channel, cb) {
Env.evPinnedPadsReady.reg(() => { Env.evPinnedPadsReady.reg(() => {
if (Env.pinnedPads[channel] && Object.keys(Env.pinnedPads[channel]).length) { if (Env.pinnedPads[channel] && Object.keys(Env.pinnedPads[channel]).length) {
cb(true); cb(void 0, true);
} else { } else {
delete Env.pinnedPads[channel]; delete Env.pinnedPads[channel]; // XXX WAT
cb(false); cb(void 0, false);
} }
}); });
}; };

@ -2,7 +2,6 @@
/* globals Buffer*/ /* globals Buffer*/
const Quota = module.exports; const Quota = module.exports;
const Core = require("./core");
const Util = require("../common-util"); const Util = require("../common-util");
const Package = require('../../package.json'); const Package = require('../../package.json');
const Https = require("https"); const Https = require("https");
@ -35,25 +34,12 @@ Quota.applyCustomLimits = function (Env) {
}); });
}; };
// The limits object contains storage limits for all the publicKey that have paid Quota.updateCachedLimits = function (Env, cb) {
// To each key is associated an object containing the 'limit' value and a 'note' explaining that limit
// XXX maybe the use case with a publicKey should be a different command that calls this?
Quota.updateLimits = function (Env, publicKey, cb) { // FIXME BATCH?S
if (Env.adminEmail === false) { if (Env.adminEmail === false) {
Quota.applyCustomLimits(Env); Quota.applyCustomLimits(Env);
if (Env.allowSubscriptions === false) { return; } if (Env.allowSubscriptions === false) { return; }
throw new Error("allowSubscriptions must be false if adminEmail is false"); throw new Error("allowSubscriptions must be false if adminEmail is false");
} }
if (typeof cb !== "function") { cb = function () {}; }
var defaultLimit = typeof(Env.defaultStorageLimit) === 'number'?
Env.defaultStorageLimit: Core.DEFAULT_LIMIT;
var userId;
if (publicKey) {
userId = Util.unescapeKeyCharacters(publicKey);
}
var body = JSON.stringify({ var body = JSON.stringify({
domain: Env.myDomain, domain: Env.myDomain,
@ -86,14 +72,7 @@ Quota.updateLimits = function (Env, publicKey, cb) { // FIXME BATCH?S
var json = JSON.parse(str); var json = JSON.parse(str);
Env.limits = json; Env.limits = json;
Quota.applyCustomLimits(Env); Quota.applyCustomLimits(Env);
cb(void 0);
var l;
if (userId) {
var limit = Env.limits[userId];
l = limit && typeof limit.limit === "number" ?
[limit.limit, limit.plan, limit.note] : [defaultLimit, '', ''];
}
cb(void 0, l);
} catch (e) { } catch (e) {
cb(e); cb(e);
} }
@ -109,4 +88,19 @@ Quota.updateLimits = function (Env, publicKey, cb) { // FIXME BATCH?S
req.end(body); req.end(body);
}; };
// The limits object contains storage limits for all the publicKey that have paid
// To each key is associated an object containing the 'limit' value and a 'note' explaining that limit
Quota.getUpdatedLimit = function (Env, safeKey, cb) { // FIXME BATCH?S
Quota.updateCachedLimits(Env, function (err) {
if (err) { return void cb(err); }
var limit = Env.limits[safeKey];
if (limit && typeof(limit.limit) === 'number') {
return void cb(void 0, [limit.limit, limit.plan, limit.note]);
}
return void cb(void 0, [Env.defaultStorageLimit, '', '']);
});
};

@ -595,7 +595,9 @@ module.exports.create = function (cfg, cb) {
const start = (beforeHash) ? 0 : offset; const start = (beforeHash) ? 0 : offset;
store.readMessagesBin(channelName, start, (msgObj, readMore, abort) => { store.readMessagesBin(channelName, start, (msgObj, readMore, abort) => {
if (beforeHash && msgObj.offset >= offset) { return void abort(); } if (beforeHash && msgObj.offset >= offset) { return void abort(); }
handler(tryParse(msgObj.buff.toString('utf8')), readMore); var parsed = tryParse(msgObj.buff.toString('utf8'));
if (!parsed) { return void readMore(); }
handler(parsed, readMore);
}, waitFor(function (err) { }, waitFor(function (err) {
return void cb(err); return void cb(err);
})); }));
@ -749,7 +751,6 @@ module.exports.create = function (cfg, cb) {
// TODO compute lastKnownHash in a manner such that it will always skip past the metadata line? // TODO compute lastKnownHash in a manner such that it will always skip past the metadata line?
getHistoryAsync(channelName, lastKnownHash, false, (msg, readMore) => { getHistoryAsync(channelName, lastKnownHash, false, (msg, readMore) => {
if (!msg) { return; } // XXX
msgCount++; msgCount++;
// avoid sending the metadata message a second time // avoid sending the metadata message a second time
if (isMetadataMessage(msg) && metadata_cache[channelName]) { return readMore(); } if (isMetadataMessage(msg) && metadata_cache[channelName]) { return readMore(); }
@ -869,7 +870,6 @@ module.exports.create = function (cfg, cb) {
// FIXME should we send metadata here too? // FIXME should we send metadata here too?
// none of the clientside code which uses this API needs metadata, but it won't hurt to send it (2019-08-22) // none of the clientside code which uses this API needs metadata, but it won't hurt to send it (2019-08-22)
return void getHistoryAsync(parsed[1], -1, false, (msg, readMore) => { return void getHistoryAsync(parsed[1], -1, false, (msg, readMore) => {
if (!msg) { return; }
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(['FULL_HISTORY', msg])], readMore); Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(['FULL_HISTORY', msg])], readMore);
}, (err) => { }, (err) => {
let parsedMsg = ['FULL_HISTORY_END', parsed[1]]; let parsedMsg = ['FULL_HISTORY_END', parsed[1]];

@ -18,98 +18,30 @@ var RPC = module.exports;
const Store = require("../storage/file"); const Store = require("../storage/file");
const BlobStore = require("../storage/blob"); const BlobStore = require("../storage/blob");
const UNAUTHENTICATED_CALLS = [ const UNAUTHENTICATED_CALLS = {
'GET_FILE_SIZE', GET_FILE_SIZE: Pinning.getFileSize, // XXX TEST
'GET_METADATA', GET_MULTIPLE_FILE_SIZE: Pinning.getMultipleFileSize,
'GET_MULTIPLE_FILE_SIZE', GET_DELETED_PADS: Pinning.getDeletedPads,
'IS_CHANNEL_PINNED', IS_CHANNEL_PINNED: Pinning.isChannelPinned,
'IS_NEW_CHANNEL', IS_NEW_CHANNEL: Channel.isNewChannel,
'GET_DELETED_PADS', WRITE_PRIVATE_MESSAGE: Channel.writePrivateMessage,
'WRITE_PRIVATE_MESSAGE',
];
var isUnauthenticatedCall = function (call) {
return UNAUTHENTICATED_CALLS.indexOf(call) !== -1;
};
const AUTHENTICATED_CALLS = [
'COOKIE',
'RESET',
'PIN',
'UNPIN',
'GET_HASH',
'GET_TOTAL_SIZE',
'UPDATE_LIMITS',
'GET_LIMIT',
'UPLOAD_STATUS',
'UPLOAD_COMPLETE',
'OWNED_UPLOAD_COMPLETE',
'UPLOAD_CANCEL',
'EXPIRE_SESSION',
'TRIM_HISTORY',
'CLEAR_OWNED_CHANNEL',
'REMOVE_OWNED_CHANNEL',
'REMOVE_PINS',
'TRIM_PINS',
'WRITE_LOGIN_BLOCK',
'REMOVE_LOGIN_BLOCK',
'ADMIN',
'SET_METADATA'
];
var isAuthenticatedCall = function (call) {
return AUTHENTICATED_CALLS.indexOf(call) !== -1;
}; };
var isUnauthenticateMessage = function (msg) { var isUnauthenticateMessage = function (msg) {
return msg && msg.length === 2 && isUnauthenticatedCall(msg[0]); return msg && msg.length === 2 && typeof(UNAUTHENTICATED_CALLS[msg[0]]) === 'function';
}; };
var handleUnauthenticatedMessage = function (Env, msg, respond, Server) { var handleUnauthenticatedMessage = function (Env, msg, respond, Server) {
Env.Log.silly('LOG_RPC', msg[0]); Env.Log.silly('LOG_RPC', msg[0]);
switch (msg[0]) {
case 'GET_FILE_SIZE': var method = UNAUTHENTICATED_CALLS[msg[0]];
return void Pinning.getFileSize(Env, msg[1], function (e, size) { method(Env, msg[1], function (err, value) {
Env.WARN(e, msg[1]); if (err) {
respond(e, [null, size, null]); Env.WARN(err, msg[1]);
}); return void respond(err);
case 'GET_METADATA': }
return void Metadata.getMetadata(Env, msg[1], function (e, data) { respond(err, [null, value, null]);
Env.WARN(e, msg[1]); }, Server);
respond(e, [null, data, null]);
});
case 'GET_MULTIPLE_FILE_SIZE': // XXX not actually used on the client?
return void Pinning.getMultipleFileSize(Env, msg[1], function (e, dict) {
if (e) {
Env.WARN(e, dict);
return respond(e);
}
respond(e, [null, dict, null]);
});
case 'GET_DELETED_PADS':
return void Pinning.getDeletedPads(Env, msg[1], function (e, list) {
if (e) {
Env.WARN(e, msg[1]);
return respond(e);
}
respond(e, [null, list, null]);
});
case 'IS_CHANNEL_PINNED':
return void Pinning.isChannelPinned(Env, msg[1], function (isPinned) {
respond(null, [null, isPinned, null]);
});
case 'IS_NEW_CHANNEL':
return void Channel.isNewChannel(Env, msg[1], function (e, isNew) {
respond(e, [null, isNew, null]);
});
case 'WRITE_PRIVATE_MESSAGE':
return void Channel.writePrivateMessage(Env, msg[1], Server, function (e, output) {
respond(e, output);
});
default:
Env.Log.warn("UNSUPPORTED_RPC_CALL", msg);
return respond('UNSUPPORTED_RPC_CALL', msg);
}
}; };
const AUTHENTICATED_USER_TARGETED = { const AUTHENTICATED_USER_TARGETED = {
@ -124,24 +56,47 @@ const AUTHENTICATED_USER_TARGETED = {
UPLOAD_COMPLETE: Upload.complete, UPLOAD_COMPLETE: Upload.complete,
UPLOAD_CANCEL: Upload.cancel, UPLOAD_CANCEL: Upload.cancel,
OWNED_UPLOAD_COMPLETE: Upload.complete_owned, OWNED_UPLOAD_COMPLETE: Upload.complete_owned,
WRITE_LOGIN_BLOCK: Block.writeLoginBlock,
REMOVE_LOGIN_BLOCK: Block.removeLoginBlock,
ADMIN: Admin.command,
}; };
const AUTHENTICATED_USER_SCOPED = { const AUTHENTICATED_USER_SCOPED = {
GET_HASH: Pinning.getHash, GET_HASH: Pinning.getHash,
GET_TOTAL_SIZE: Pinning.getTotalSize, GET_TOTAL_SIZE: Pinning.getTotalSize,
UPDATE_LIMITS: Quota.updateLimits, UPDATE_LIMITS: Quota.getUpdatedLimit,
GET_LIMIT: Pinning.getLimit, GET_LIMIT: Pinning.getLimit,
EXPIRE_SESSION: Core.expireSessionAsync, EXPIRE_SESSION: Core.expireSessionAsync,
REMOVE_PINS: Pinning.removePins, REMOVE_PINS: Pinning.removePins,
TRIM_PINS: Pinning.trimPins, TRIM_PINS: Pinning.trimPins,
SET_METADATA: Metadata.setMetadata, SET_METADATA: Metadata.setMetadata,
COOKIE: Core.haveACookie,
};
var isAuthenticatedCall = function (call) {
if (call === 'UPLOAD') { return false; }
return typeof(AUTHENTICATED_USER_TARGETED[call] || AUTHENTICATED_USER_SCOPED[call]) === 'function';
}; };
var handleAuthenticatedMessage = function (Env, map) { var handleAuthenticatedMessage = function (Env, unsafeKey, msg, respond, Server) {
var msg = map.msg; /* If you have gotten this far, you have signed the message with the
var safeKey = map.safeKey; public key which you provided.
var Respond = map.Respond; */
var Server = map.Server;
var safeKey = Util.escapeKeyCharacters(unsafeKey);
var Respond = function (e, value) {
var session = Env.Sessions[safeKey];
var token = session? session.tokens.slice(-1)[0]: '';
var cookie = Core.makeCookie(token).join('|');
respond(e ? String(e): e, [cookie].concat(typeof(value) !== 'undefined' ?value: []));
};
msg.shift();
// discard validated cookie from message
if (!msg.length) {
return void Respond('INVALID_MSG');
}
var TYPE = msg[0]; var TYPE = msg[0];
@ -151,7 +106,7 @@ var handleAuthenticatedMessage = function (Env, map) {
return void AUTHENTICATED_USER_TARGETED[TYPE](Env, safeKey, msg[1], function (e, value) { return void AUTHENTICATED_USER_TARGETED[TYPE](Env, safeKey, msg[1], function (e, value) {
Env.WARN(e, value); Env.WARN(e, value);
return void Respond(e, value); return void Respond(e, value);
}); }, Server);
} }
if (typeof(AUTHENTICATED_USER_SCOPED[TYPE]) === 'function') { if (typeof(AUTHENTICATED_USER_SCOPED[TYPE]) === 'function') {
@ -164,35 +119,7 @@ var handleAuthenticatedMessage = function (Env, map) {
}); });
} }
switch (msg[0]) { return void Respond('UNSUPPORTED_RPC_CALL', msg);
case 'COOKIE': return void Respond(void 0);
case 'WRITE_LOGIN_BLOCK':
return void Block.writeLoginBlock(Env, msg[1], function (e) { // XXX SPECIAL
if (e) {
Env.WARN(e, 'WRITE_LOGIN_BLOCK');
return void Respond(e);
}
Respond(e);
});
case 'REMOVE_LOGIN_BLOCK':
return void Block.removeLoginBlock(Env, msg[1], function (e) { // XXX SPECIAL
if (e) {
Env.WARN(e, 'REMOVE_LOGIN_BLOCK');
return void Respond(e);
}
Respond(e);
});
case 'ADMIN':
return void Admin.command(Env, Server, safeKey, msg[1], function (e, result) { // XXX SPECIAL
if (e) {
Env.WARN(e, result);
return void Respond(e);
}
Respond(void 0, result);
});
default:
return void Respond('UNSUPPORTED_RPC_CALL', msg);
}
}; };
var rpc = function (Env, Server, data, respond) { var rpc = function (Env, Server, data, respond) {
@ -241,45 +168,23 @@ var rpc = function (Env, Server, data, respond) {
return void respond('INVALID_MESSAGE_OR_PUBLIC_KEY'); return void respond('INVALID_MESSAGE_OR_PUBLIC_KEY');
} }
if (isAuthenticatedCall(msg[1])) { var command = msg[1];
if (Core.checkSignature(Env, serialized, signature, publicKey) !== true) {
return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
}
} else if (msg[1] !== 'UPLOAD') {
Env.Log.warn('INVALID_RPC_CALL', msg[1]);
return void respond("INVALID_RPC_CALL");
}
var safeKey = Util.escapeKeyCharacters(publicKey);
/* If you have gotten this far, you have signed the message with the
public key which you provided.
We can safely modify the state for that key
OR it's an unauthenticated call, which must not modify the state
for that key in a meaningful way.
*/
// discard validated cookie from message
msg.shift();
var Respond = function (e, msg) {
var session = Env.Sessions[safeKey];
var token = session? session.tokens.slice(-1)[0]: '';
var cookie = Core.makeCookie(token).join('|');
respond(e ? String(e): e, [cookie].concat(typeof(msg) !== 'undefined' ?msg: []));
};
if (typeof(msg) !== 'object' || !msg.length) { if (command === 'UPLOAD') {
return void Respond('INVALID_MSG'); // UPLOAD is a special case that skips signature validation
// intentional fallthrough behaviour
return void handleAuthenticatedMessage(Env, publicKey, msg, respond, Server);
} }
if (isAuthenticatedCall(command)) {
handleAuthenticatedMessage(Env, { // check the signature on the message
msg: msg, // refuse the command if it doesn't validate
safeKey: safeKey, if (Core.checkSignature(Env, serialized, signature, publicKey) === true) {
Respond: Respond, return void handleAuthenticatedMessage(Env, publicKey, msg, respond, Server);
Server: Server, }
}); return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
}
Env.Log.warn('INVALID_RPC_CALL', command);
return void respond("INVALID_RPC_CALL");
}; };
RPC.create = function (config, cb) { RPC.create = function (config, cb) {
@ -302,10 +207,13 @@ RPC.create = function (config, cb) {
} }
}; };
if (typeof(config.domain) !== 'undefined') {
throw new Error('fuck');
}
var Env = { var Env = {
historyKeeper: config.historyKeeper, historyKeeper: config.historyKeeper,
intervals: config.intervals || {}, intervals: config.intervals || {},
defaultStorageLimit: config.defaultStorageLimit,
maxUploadSize: config.maxUploadSize || (20 * 1024 * 1024), maxUploadSize: config.maxUploadSize || (20 * 1024 * 1024),
Sessions: {}, Sessions: {},
paths: {}, paths: {},
@ -326,6 +234,10 @@ RPC.create = function (config, cb) {
domain: config.domain // XXX domain: config.domain // XXX
}; };
Env.defaultStorageLimit = typeof(config.defaultStorageLimit) === 'number' && config.defaultStorageLimit > 0?
config.defaultStorageLimit:
Core.DEFAULT_LIMIT;
try { try {
Env.admins = (config.adminKeys || []).map(function (k) { Env.admins = (config.adminKeys || []).map(function (k) {
k = k.replace(/\/+$/, ''); k = k.replace(/\/+$/, '');
@ -345,7 +257,7 @@ RPC.create = function (config, cb) {
paths.blob = keyOrDefaultString('blobPath', './blob'); paths.blob = keyOrDefaultString('blobPath', './blob');
var updateLimitDaily = function () { var updateLimitDaily = function () {
Quota.updateLimits(Env, undefined, function (e) { Quota.updateCachedLimits(Env, function (e) {
if (e) { if (e) {
WARN('limitUpdate', e); WARN('limitUpdate', e);
} }

@ -159,6 +159,13 @@ var createUser = function (config, cb) {
} }
wc.leave(); wc.leave();
})); }));
}).nThen(function (w) {
// give the server time to write your mailbox data before checking that it's correct
// XXX chainpad-server sends an ACK before the channel has actually been created
// causing you to think that everything is good.
// without this timeout the GET_METADATA rpc occasionally returns before
// the metadata has actually been written to the disk.
setTimeout(w(), 500);
}).nThen(function (w) { }).nThen(function (w) {
// confirm that you own your mailbox // confirm that you own your mailbox
user.anonRpc.send("GET_METADATA", user.mailboxChannel, w(function (err, data) { user.anonRpc.send("GET_METADATA", user.mailboxChannel, w(function (err, data) {

@ -3386,10 +3386,7 @@ define([
if (sfId) { if (sfId) {
var sfData = manager.getSharedFolderData(sfId); var sfData = manager.getSharedFolderData(sfId);
var parsed = Hash.parsePadUrl(sfData.href); var parsed = Hash.parsePadUrl(sfData.href);
sframeChan.event('EV_DRIVE_SET_HASH', parsed.hash || '');
createShareButton(sfId, $toolbar.find('.cp-app-drive-toolbar-leftside')); createShareButton(sfId, $toolbar.find('.cp-app-drive-toolbar-leftside'));
} else {
sframeChan.event('EV_DRIVE_SET_HASH', '');
} }

@ -97,18 +97,6 @@ define([
cb(obj); cb(obj);
}); });
}); });
sframeChan.on('EV_DRIVE_SET_HASH', function (/*hash*/) {
// Update the hash in the address bar
// XXX Hidden hash: don't put the shared folder href in the address bar
/*
if (!Utils.LocalStore.isLoggedIn()) { return; }
var ohc = window.onhashchange;
window.onhashchange = function () {};
window.location.hash = hash || '';
window.onhashchange = ohc;
ohc({reset:true});
*/
});
Cryptpad.onNetworkDisconnect.reg(function () { Cryptpad.onNetworkDisconnect.reg(function () {
sframeChan.event('EV_NETWORK_DISCONNECT'); sframeChan.event('EV_NETWORK_DISCONNECT');
}); });

@ -72,9 +72,6 @@ define([
cb(obj); cb(obj);
}); });
}); });
sframeChan.on('EV_DRIVE_SET_HASH', function () {
return;
});
Cryptpad.onNetworkDisconnect.reg(function () { Cryptpad.onNetworkDisconnect.reg(function () {
sframeChan.event('EV_NETWORK_DISCONNECT'); sframeChan.event('EV_NETWORK_DISCONNECT');
}); });

Loading…
Cancel
Save