You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
doushio/server/caps.js

221 lines
5.2 KiB

var async = require('async'),
authcommon = require('../admin/common'),
check = require('./msgcheck').check,
common = require('../common'),
config = require('../config'),
db = require('../db'),
hooks = require('../hooks');
var RANGES = require('./state').dbCache.ranges;
function can_access_board(ident, board) {
if (board == 'graveyard' && can_administrate(ident))
return true;
if (board == config.STAFF_BOARD && !can_moderate(ident))
return false;
if (ident.ban || ident.suspension)
return false;
if (!temporal_access_check(ident, board))
return false;
return db.is_board(board);
}
exports.can_access_board = can_access_board;
exports.can_access_thread = function (ident, op) {
var tags = db.tags_of(op);
if (!tags)
return false;
for (var i = 0; i < tags.length; i++)
if (can_access_board(ident, tags[i]))
return tags[i];
return false;
};
function temporal_access_check(ident, board) {
var info = {ident: ident, board: board, access: true};
hooks.trigger_sync('temporalAccessCheck', info);
return info.access;
}
exports.temporal_access_check = temporal_access_check;
exports.can_ever_access_board = function (ident, board) {
if (can_access_board(ident, board))
return true;
if (!temporal_access_check(ident, board))
return true;
return false;
};
function can_moderate(ident) {
return (ident.auth === 'Admin' || ident.auth === 'Moderator');
}
exports.can_moderate = can_moderate;
function can_administrate(ident) {
return ident.auth === 'Admin';
}
exports.can_administrate = can_administrate;
function denote_priv(info) {
if (info.data.priv)
info.header.push(' (priv)');
}
function dead_media_paths(paths) {
paths.src = '../dead/src/';
paths.thumb = '../dead/thumb/';
paths.mid = '../dead/mid/';
}
exports.augment_oneesama = function (oneeSama, opts) {
var ident = opts.ident;
oneeSama.ident = ident;
if (can_moderate(ident))
oneeSama.hook('headerName', authcommon.append_mnemonic);
if (can_administrate(ident)) {
oneeSama.hook('headerName', denote_priv);
oneeSama.hook('headerName', authcommon.denote_hidden);
}
if (can_administrate(ident) && opts.board == 'graveyard')
oneeSama.hook('mediaPaths', dead_media_paths);
};
exports.mod_handler = function (func) {
return function (nums, client) {
if (!can_moderate(client.ident))
return false;
var opts = nums.shift();
if (!check({when: 'string'}, opts) || !check('id...', nums))
return false;
if (!(opts.when in authcommon.delayDurations))
return false;
var delay = authcommon.delayDurations[opts.when];
if (!delay)
func(nums, client);
else
setTimeout(func.bind(null, nums, client), delay*1000);
return true;
};
};
function parse_ip(ip) {
var m = ip.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)(?:\/(\d+))?$/);
if (!m)
return false;
// damn you signed int32s!
var num = 0;
for (var i = 4, shift = 1; i > 0; i--) {
num += parseInt(m[i], 10) * shift;
shift *= 256;
}
var info = {full: ip, num: num};
if (m[5]) {
var bits = parseInt(m[5], 10);
if (bits > 0 && bits <= 32) {
info.mask = 0x100000000 - Math.pow(2, 32 - bits);
info.num &= info.mask;
}
}
return info;
}
function parse_ranges(ranges) {
if (!ranges)
return [];
ranges = ranges.map(function (o) {
if (typeof o == 'object') {
o.ip = parse_ip(o.ip);
return o;
}
else
return {ip: parse_ip(o)};
});
ranges.sort(function (a, b) { return a.ip.num - b.ip.num; });
return ranges;
}
function range_lookup(ranges, num) {
if (!ranges)
return null;
/* Ideally would have a tree lookup here or something */
var result = null;
for (var i = 0; i < ranges.length; i++) {
var box = ranges[i].ip;
/* sint32 issue here doesn't matter for realistic ranges */
if ((box.mask ? (num & box.mask) : num) === box.num)
result = ranges[i];
/* don't break out of loop */
}
return result;
}
hooks.hook('reloadHot', function (hot, cb) {
var r = global.redis;
async.forEach(authcommon.suspensionKeys, function (key, cb) {
global.redis.smembers('hot:' + key, function (err, ranges) {
if (err)
return cb(err);
if (key == 'suspensions')
ranges = parse_suspensions(ranges);
var up = key.toUpperCase();
hot[up] = (hot[up] || []).concat(ranges || []);
RANGES[key] = parse_ranges(hot[up]);
cb(null);
});
}, cb);
});
function parse_suspensions(suspensions) {
if (!suspensions)
return [];
var parsed = [];
suspensions.forEach(function (s) {
try {
parsed.push(JSON.parse(s));
}
catch (e) {
winston.error("Bad suspension JSON: " + s);
}
});
return parsed;
}
exports.lookup_ident = function (ip, country) {
var ident = {ip, country, readOnly: config.READ_ONLY};
if (country
&& config.RESTRICTED_COUNTRIES
&& config.RESTRICTED_COUNTRIES.indexOf(country) >= 0) {
ident.readOnly = true;
}
var num = parse_ip(ip).num;
var ban = range_lookup(RANGES.bans, num);
if (ban) {
ident.ban = ban.ip.full;
return ident;
}
ban = range_lookup(RANGES.timeouts, num);
if (ban) {
ident.ban = ban.ip.full;
ident.timeout = true;
return ident;
}
var suspension = range_lookup(RANGES.suspensions, num);
if (suspension) {
ident.suspension = suspension;
return ident;
}
var priv = range_lookup(RANGES.boxes, num);
if (priv)
ident.priv = priv.ip.full;
var slow = range_lookup(RANGES.slows, num);
if (slow)
ident.slow = slow;
return ident;
};