Merge branch 'staging' into communities-trim
This commit is contained in:
commit
9a857ea058
39
CHANGELOG.md
39
CHANGELOG.md
@ -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]];
|
||||||
|
|||||||
226
lib/rpc.js
226
lib/rpc.js
@ -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) {
|
|
||||||
Env.WARN(e, msg[1]);
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
respond(err, [null, value, null]);
|
||||||
|
}, Server);
|
||||||
};
|
};
|
||||||
|
|
||||||
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 handleAuthenticatedMessage = function (Env, map) {
|
var isAuthenticatedCall = function (call) {
|
||||||
var msg = map.msg;
|
if (call === 'UPLOAD') { return false; }
|
||||||
var safeKey = map.safeKey;
|
return typeof(AUTHENTICATED_USER_TARGETED[call] || AUTHENTICATED_USER_SCOPED[call]) === 'function';
|
||||||
var Respond = map.Respond;
|
};
|
||||||
var Server = map.Server;
|
|
||||||
|
var handleAuthenticatedMessage = function (Env, unsafeKey, msg, respond, Server) {
|
||||||
|
/* If you have gotten this far, you have signed the message with the
|
||||||
|
public key which you provided.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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]) {
|
|
||||||
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);
|
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) {
|
|
||||||
|
if (command === 'UPLOAD') {
|
||||||
|
// UPLOAD is a special case that skips signature validation
|
||||||
|
// intentional fallthrough behaviour
|
||||||
|
return void handleAuthenticatedMessage(Env, publicKey, msg, respond, Server);
|
||||||
|
}
|
||||||
|
if (isAuthenticatedCall(command)) {
|
||||||
|
// check the signature on the message
|
||||||
|
// refuse the command if it doesn't validate
|
||||||
|
if (Core.checkSignature(Env, serialized, signature, publicKey) === true) {
|
||||||
|
return void handleAuthenticatedMessage(Env, publicKey, msg, respond, Server);
|
||||||
|
}
|
||||||
return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
|
return void respond("INVALID_SIGNATURE_OR_PUBLIC_KEY");
|
||||||
}
|
}
|
||||||
} else if (msg[1] !== 'UPLOAD') {
|
Env.Log.warn('INVALID_RPC_CALL', command);
|
||||||
Env.Log.warn('INVALID_RPC_CALL', msg[1]);
|
|
||||||
return void respond("INVALID_RPC_CALL");
|
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) {
|
|
||||||
return void Respond('INVALID_MSG');
|
|
||||||
}
|
|
||||||
|
|
||||||
handleAuthenticatedMessage(Env, {
|
|
||||||
msg: msg,
|
|
||||||
safeKey: safeKey,
|
|
||||||
Respond: Respond,
|
|
||||||
Server: Server,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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…
x
Reference in New Issue
Block a user