Merge branch '3.0' into uws

This commit is contained in:
Calvin Montgomery 2018-07-26 21:02:01 -07:00
commit 17cf6023db
7 changed files with 181 additions and 109 deletions

View File

@ -2,7 +2,7 @@
"author": "Calvin Montgomery",
"name": "CyTube",
"description": "Online media synchronizer and chat",
"version": "3.56.3",
"version": "3.56.5",
"repository": {
"url": "http://github.com/calzoneman/sync"
},

View File

@ -79,7 +79,9 @@ PollModule.prototype.onUserPostJoin = function (user) {
this.addUserToPollRoom(user);
const self = this;
user.on("effectiveRankChange", () => {
if (self.channel && !self.channel.dead) {
self.addUserToPollRoom(user);
}
});
};

View File

@ -386,6 +386,12 @@ function preprocessConfig(cfg) {
return contact.name !== 'calzoneman';
});
if (!cfg.io.throttle) {
cfg.io.throttle = {
'in-rate-limit': Infinity
};
}
return cfg;
}

View File

@ -103,8 +103,14 @@ function translateStatusCode(statusCode) {
"the file to be downloaded.";
case 404:
return "The requested link could not be found (404).";
case 405:
return "The website hosting the link does not support HEAD requests, " +
"so the link could not be retrieved.";
case 410:
return "The requested link does not exist (410 Gone).";
case 501:
return "The requested link could not be retrieved because the server " +
"hosting it does not support CyTube's request.";
case 500:
case 503:
return "The website hosting the audio/video link encountered an error " +
@ -143,6 +149,8 @@ function testUrl(url, cb, params = { redirCount: 0, cookie: '' }) {
if (cookie) {
data.headers = { 'Cookie': cookie };
}
try {
var req = transport.request(data, function (res) {
req.abort();
@ -205,6 +213,12 @@ function testUrl(url, cb, params = { redirCount: 0, cookie: '' }) {
});
req.end();
} catch (error) {
LOGGER.error('Unable to make raw file probe request: %s', error.stack);
cb("An unexpected error occurred while trying to process the link. " +
"Try again, and contact support for further troubleshooting if the " +
"problem continues.");
}
}
function readOldFormat(buf) {

View File

@ -233,6 +233,8 @@ class IOServer {
return;
}
this.setRateLimiter(socket);
emitMetrics(socket);
LOGGER.info('Accepted socket from %s', socket.context.ipAddress);
@ -250,6 +252,25 @@ class IOServer {
}
}
setRateLimiter(socket) {
const thunk = () => Config.get('io.throttle.in-rate-limit');
socket._inRateLimit = new TokenBucket(thunk, thunk);
socket.on('cytube:count-event', () => {
if (socket._inRateLimit.throttle()) {
LOGGER.warn(
'Kicking client %s: exceeded in-rate-limit of %d',
socket.context.ipAddress,
thunk()
);
socket.emit('kick', { reason: 'Rate limit exceeded' });
socket.disconnect();
}
});
}
initSocketIO() {
patchSocketMetrics();
patchTypecheckedFunctions();
@ -306,10 +327,12 @@ const outgoingPacketCount = new Counter({
function patchSocketMetrics() {
const onevent = Socket.prototype.onevent;
const packet = Socket.prototype.packet;
const emit = require('events').EventEmitter.prototype.emit;
Socket.prototype.onevent = function patchedOnevent() {
onevent.apply(this, arguments);
incomingEventCount.inc(1);
emit.call(this, 'cytube:count-event');
};
Socket.prototype.packet = function patchedPacket() {

View File

@ -1,16 +1,27 @@
class TokenBucket {
constructor(capacity, refillRate) {
if (typeof refillRate !== 'function') {
const _refillRate = refillRate;
refillRate = () => _refillRate;
}
if (typeof capacity !== 'function') {
const _capacity = capacity;
capacity = () => _capacity;
}
this.capacity = capacity;
this.refillRate = refillRate;
this.count = capacity;
this.count = capacity();
this.lastRefill = Date.now();
}
throttle() {
const now = Date.now();
const delta = Math.floor((now - this.lastRefill) / 1000 * this.refillRate);
const delta = Math.floor(
(now - this.lastRefill) / 1000 * this.refillRate()
);
if (delta > 0) {
this.count = Math.min(this.capacity, this.count + delta);
this.count = Math.min(this.capacity(), this.count + delta);
this.lastRefill = now;
}

View File

@ -13,6 +13,7 @@ var Config = require("../config");
var session = require("../session");
var csrf = require("./csrf");
const url = require("url");
import crypto from 'crypto';
const LOGGER = require('@calzoneman/jsli')('web/accounts');
@ -536,24 +537,38 @@ function handlePasswordReset(req, res) {
return;
}
if (actualEmail !== email.trim()) {
if (actualEmail === '') {
sendPug(res, "account-passwordreset", {
reset: false,
resetEmail: "",
resetErr: `Username ${name} cannot be recovered because it ` +
"doesn't have an email address associated with it."
});
return;
} else if (actualEmail.toLowerCase() !== email.trim().toLowerCase()) {
sendPug(res, "account-passwordreset", {
reset: false,
resetEmail: "",
resetErr: "Provided email does not match the email address on record for " + name
});
return;
} else if (actualEmail === "") {
}
crypto.randomBytes(20, (err, bytes) => {
if (err) {
LOGGER.error(
'Could not generate random bytes for password reset: %s',
err.stack
);
sendPug(res, "account-passwordreset", {
reset: false,
resetEmail: "",
resetErr: name + " doesn't have an email address on record. Please contact an " +
"administrator to manually reset your password."
resetEmail: email,
resetErr: "Internal error when generating password reset"
});
return;
}
var hash = $util.sha1($util.randomSalt(64));
var hash = bytes.toString('hex');
// 24-hour expiration
var expire = Date.now() + 86400000;
var ip = req.realIP;
@ -561,7 +576,7 @@ function handlePasswordReset(req, res) {
db.addPasswordReset({
ip: ip,
name: name,
email: email,
email: actualEmail,
hash: hash,
expire: expire
}, function (err, _dbres) {
@ -610,6 +625,7 @@ function handlePasswordReset(req, res) {
});
});
});
});
}
/**