|
|
|
/*jshint esversion: 6 */
|
|
|
|
/* globals Buffer*/
|
|
|
|
const Quota = module.exports;
|
|
|
|
|
|
|
|
//const Util = require("../common-util");
|
|
|
|
const Keys = require("../keys");
|
|
|
|
const Package = require('../../package.json');
|
|
|
|
const Https = require("https");
|
|
|
|
const Util = require("../common-util");
|
|
|
|
|
|
|
|
var validLimitFields = ['limit', 'plan', 'note', 'users', 'origin'];
|
|
|
|
|
|
|
|
Quota.isValidLimit = function (o) {
|
|
|
|
var valid = o && typeof(o) === 'object' &&
|
|
|
|
typeof(o.limit) === 'number' &&
|
|
|
|
typeof(o.plan) === 'string' &&
|
|
|
|
typeof(o.note) === 'string' &&
|
|
|
|
// optionally contains a 'users' array
|
|
|
|
(Array.isArray(o.users) || typeof(o.users) === 'undefined') &&
|
|
|
|
// check that the object contains only the expected fields
|
|
|
|
!Object.keys(o).some(function (k) {
|
|
|
|
return validLimitFields.indexOf(k) === -1;
|
|
|
|
});
|
|
|
|
|
|
|
|
return valid;
|
|
|
|
};
|
|
|
|
|
|
|
|
Quota.applyCustomLimits = function (Env) {
|
|
|
|
// DecreedLimits > customLimits > serverLimits;
|
|
|
|
|
|
|
|
// XXX perform an integrity check on shared limits
|
|
|
|
// especially relevant because we use Env.limits
|
|
|
|
// when considering whether to archive inactive accounts
|
|
|
|
|
|
|
|
// read custom limits from the Environment (taken from config)
|
|
|
|
var customLimits = (function (custom) {
|
|
|
|
var limits = {};
|
|
|
|
Object.keys(custom).forEach(function (k) {
|
|
|
|
var user;
|
|
|
|
|
|
|
|
// XXX this length (44) is also hardcoded in decrees.js
|
|
|
|
// before calling Keys.parseUser.
|
|
|
|
// Maybe we should improve Keys.parseUser to handle this
|
|
|
|
// option directly
|
|
|
|
if (k.length === 44) {
|
|
|
|
user = {
|
|
|
|
pubkey: Util.unescapeKeyCharacters(k)
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
user = Keys.parseUser(k);
|
|
|
|
} catch (err) {
|
|
|
|
return void Env.Log.error("PARSE_CUSTOM_LIMIT_BLOCK", {
|
|
|
|
user: k,
|
|
|
|
error: err.message,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var unsafeKey = user.pubkey;
|
|
|
|
limits[unsafeKey] = custom[k];
|
|
|
|
});
|
|
|
|
return limits;
|
|
|
|
}(Env.customLimits || {}));
|
|
|
|
|
|
|
|
Env.limits = Env.limits || {};
|
|
|
|
Object.keys(customLimits).forEach(function (k) {
|
|
|
|
if (!Quota.isValidLimit(customLimits[k])) { return; }
|
|
|
|
Env.limits[k] = customLimits[k];
|
|
|
|
});
|
|
|
|
// console.log(Env.limits);
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Env = {
|
|
|
|
myDomain,
|
|
|
|
mySubdomain,
|
|
|
|
adminEmail,
|
|
|
|
Package.version,
|
|
|
|
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
var queryAccountServer = function (Env, cb) {
|
|
|
|
var done = Util.once(Util.mkAsync(cb));
|
|
|
|
|
|
|
|
var body = JSON.stringify({
|
|
|
|
domain: Env.myDomain,
|
|
|
|
subdomain: Env.mySubdomain || null,
|
|
|
|
adminEmail: Env.adminEmail,
|
|
|
|
version: Package.version
|
|
|
|
});
|
|
|
|
var options = {
|
|
|
|
host: 'accounts.cryptpad.fr',
|
|
|
|
path: '/api/getauthorized',
|
|
|
|
method: 'POST',
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
"Content-Length": Buffer.byteLength(body)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var req = Https.request(options, function (response) {
|
|
|
|
if (!('' + response.statusCode).match(/^2\d\d$/)) {
|
|
|
|
return void cb('SERVER ERROR ' + response.statusCode);
|
|
|
|
}
|
|
|
|
var str = '';
|
|
|
|
|
|
|
|
response.on('data', function (chunk) {
|
|
|
|
str += chunk;
|
|
|
|
});
|
|
|
|
|
|
|
|
response.on('end', function () {
|
|
|
|
try {
|
|
|
|
var json = JSON.parse(str);
|
|
|
|
// don't overwrite the limits with junk data
|
|
|
|
if (json && json.message === 'EINVAL') { return void cb(); }
|
|
|
|
done(void 0, json);
|
|
|
|
} catch (e) {
|
|
|
|
done(e);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
req.on('error', function (e) {
|
|
|
|
Quota.applyCustomLimits(Env);
|
|
|
|
if (!Env.myDomain) { return done(); }
|
|
|
|
// only return an error if your server allows subscriptions
|
|
|
|
done(e);
|
|
|
|
});
|
|
|
|
|
|
|
|
req.end(body);
|
|
|
|
};
|
|
|
|
|
|
|
|
Quota.queryAccountServer = function (Env, cb) {
|
|
|
|
Env.batchAccountQuery('', cb, function (done) {
|
|
|
|
queryAccountServer(Env, done);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
Quota.updateCachedLimits = function (Env, cb) {
|
|
|
|
Quota.applyCustomLimits(Env);
|
|
|
|
if (Env.blockDailyCheck === true ||
|
|
|
|
(typeof(Env.blockDailyCheck) === 'undefined' && Env.adminEmail === false && Env.allowSubscriptions === false)) {
|
|
|
|
return void cb();
|
|
|
|
}
|
|
|
|
|
|
|
|
Quota.queryAccountServer(Env, function (err, json) {
|
|
|
|
if (err) { return void cb(err); }
|
|
|
|
if (!json) { return void cb(); }
|
|
|
|
|
|
|
|
for (var k in json) {
|
|
|
|
if (k.length === 44 && json[k]) {
|
|
|
|
json[k].origin = 'remote';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Env.limits = json;
|
|
|
|
|
|
|
|
Quota.applyCustomLimits(Env);
|
|
|
|
cb();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
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, '', '']);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|