CyTube/lib/server.js

288 lines
9.1 KiB
JavaScript
Raw Normal View History

2013-07-19 20:27:21 +00:00
var path = require("path");
2013-07-28 23:47:55 +00:00
var fs = require("fs");
2013-09-09 22:16:41 +00:00
var http = require("http");
var https = require("https");
2013-04-17 18:42:29 +00:00
var express = require("express");
2013-07-15 22:57:33 +00:00
var Config = require("./config");
var Logger = require("./logger");
var Channel = require("./channel");
var User = require("./user");
const VERSION = "2.4.2";
2013-04-25 16:00:06 +00:00
2013-07-15 22:57:33 +00:00
function getIP(req) {
2013-07-06 17:00:02 +00:00
var raw = req.connection.remoteAddress;
var forward = req.header("x-forwarded-for");
2013-09-10 21:11:35 +00:00
if((Server.cfg["trust-x-forward"] || raw === "127.0.0.1") && forward) {
2013-07-15 22:57:33 +00:00
var ip = forward.split(",")[0];
Logger.syslog.log("REVPROXY " + raw + " => " + ip);
2013-07-06 17:00:02 +00:00
return ip;
}
2013-07-06 17:00:02 +00:00
return raw;
}
function getSocketIP(socket) {
2013-07-06 17:00:02 +00:00
var raw = socket.handshake.address.address;
2013-09-10 21:11:35 +00:00
if(Server.cfg["trust-x-forward"] || raw === "127.0.0.1") {
2013-07-06 17:00:02 +00:00
if(typeof socket.handshake.headers["x-forwarded-for"] == "string") {
2013-07-15 22:57:33 +00:00
var ip = socket.handshake.headers["x-forwarded-for"]
.split(",")[0];
Logger.syslog.log("REVPROXY " + raw + " => " + ip);
2013-07-06 17:00:02 +00:00
return ip;
}
}
2013-07-15 22:57:33 +00:00
return raw;
}
2013-07-15 22:57:33 +00:00
var Server = {
channels: [],
channelLoaded: function (name) {
for(var i in this.channels) {
if(this.channels[i].canonical_name == name.toLowerCase())
2013-07-15 22:57:33 +00:00
return true;
}
return false;
},
getChannel: function (name) {
for(var i in this.channels) {
if(this.channels[i].canonical_name == name.toLowerCase())
2013-07-15 22:57:33 +00:00
return this.channels[i];
}
var c = new Channel(name, this);
this.channels.push(c);
return c;
},
unloadChannel: function(chan) {
if(chan.registered)
chan.saveDump();
chan.playlist.die();
chan.logger.close();
2013-07-15 22:57:33 +00:00
for(var i in this.channels) {
if(this.channels[i].canonical_name == chan.canonical_name) {
2013-07-15 22:57:33 +00:00
this.channels.splice(i, 1);
break;
}
}
var keys = Object.keys(chan);
for (var i in keys) {
delete chan[keys[i]];
}
chan.dead = true;
2013-07-15 22:57:33 +00:00
},
app: null,
io: null,
httpserv: null,
2013-09-09 22:16:41 +00:00
sslserv: null,
sslio: null,
2013-07-15 22:57:33 +00:00
ioserv: null,
db: null,
ips: {},
2013-07-16 03:01:12 +00:00
acp: null,
2013-07-30 13:43:58 +00:00
httpaccess: null,
2013-08-17 23:44:48 +00:00
actionlog: null,
2013-07-30 13:43:58 +00:00
logHTTP: function (req, status) {
if(status === undefined)
status = 200;
var ip = req.connection.remoteAddress;
2013-07-30 16:26:08 +00:00
var ip2 = false;
if(this.cfg["trust-x-forward"])
ip2 = req.header("x-forwarded-for") || req.header("cf-connecting-ip");
var ipstr = !ip2 ? ip : ip + " (X-Forwarded-For " + ip2 + ")";
var url = req.url;
// Remove query
if(url.indexOf("?") != -1)
url = url.substring(0, url.lastIndexOf("?"));
this.httpaccess.log([ipstr, req.method, url, status, req.headers["user-agent"]].join(" "));
2013-07-30 13:43:58 +00:00
},
2013-09-09 22:16:41 +00:00
handleIOConnection: function (socket) {
var self = this;
var ip = getSocketIP(socket);
socket._ip = ip;
self.db.isGlobalIPBanned(ip, function (err, bant) {
if(bant) {
Logger.syslog.log("Disconnecting " + ip + " - gbanned");
socket.emit("kick", {
reason: "You're globally banned."
});
socket.disconnect(true);
}
});
socket.on("disconnect", function () {
self.ips[ip]--;
}.bind(self));
if(!(ip in self.ips))
self.ips[ip] = 0;
self.ips[ip]++;
if(self.ips[ip] > Server.cfg["ip-connection-limit"]) {
socket.emit("kick", {
reason: "Too many connections from your IP address"
});
socket.disconnect(true);
return;
}
// finally a valid user
Logger.syslog.log("Accepted socket from /" + socket._ip);
new User(socket, self);
},
2013-07-15 22:57:33 +00:00
init: function () {
var self = this;
// init database
var Database = require("./database");
this.db = new Database(self.cfg);
this.db.init();
this.actionlog = require("./actionlog")(self);
this.httpaccess = new Logger.Logger(path.join(__dirname,
"../httpaccess.log"));
2013-07-15 22:57:33 +00:00
this.app = express();
2013-08-08 03:44:41 +00:00
this.app.use(express.bodyParser());
2013-07-15 22:57:33 +00:00
// channel path
self.app.get("/r/:channel(*)", function (req, res, next) {
2013-07-15 22:57:33 +00:00
var c = req.params.channel;
2013-07-30 13:43:58 +00:00
if(!c.match(/^[\w-_]+$/)) {
2013-07-15 22:57:33 +00:00
res.redirect("/" + c);
2013-07-30 13:43:58 +00:00
}
else {
self.logHTTP(req);
res.sendfile("channel.html", {
root: path.join(__dirname, "../www")
});
2013-07-30 13:43:58 +00:00
}
});
2013-07-15 22:57:33 +00:00
// api path
self.api = require("./api")(self);
self.app.get("/", function (req, res, next) {
self.logHTTP(req);
res.sendfile("index.html", {
root: path.join(__dirname, "../www")
});
});
2013-07-20 01:59:39 +00:00
2013-07-15 22:57:33 +00:00
// default path
self.app.get("/:thing(*)", function (req, res, next) {
2013-07-27 15:06:49 +00:00
var opts = {
root: path.join(__dirname, "../www"),
maxAge: self.cfg["asset-cache-ttl"]
2013-07-27 15:06:49 +00:00
}
res.sendfile(req.params.thing, opts, function (err) {
if(err) {
self.logHTTP(req, err.status);
2013-07-27 15:06:49 +00:00
// Damn path traversal attacks
if(req.params.thing.indexOf("%2e") != -1) {
res.send("Don't try that again, I'll ban you");
Logger.syslog.log("WARNING: Attempted path "+
"traversal from /" + getIP(req));
Logger.syslog.log("URL: " + req.url);
}
// Something actually went wrong
else {
// Status codes over 500 are server errors
if(err.status >= 500)
Logger.errlog.log(err);
res.send(err.status);
2013-07-27 15:06:49 +00:00
}
}
2013-07-30 13:43:58 +00:00
else {
self.logHTTP(req);
2013-07-30 13:43:58 +00:00
}
});
});
2013-07-15 22:57:33 +00:00
// fallback
self.app.use(function (err, req, res, next) {
self.logHTTP(req, err.status);
2013-07-15 22:57:33 +00:00
if(err.status == 404) {
res.send(404);
} else {
next(err);
}
});
2013-05-03 18:15:05 +00:00
2013-07-15 22:57:33 +00:00
// bind servers
2013-09-09 22:16:41 +00:00
if (self.cfg["enable-ssl"]) {
var key = fs.readFileSync(path.resolve(__dirname, "..",
self.cfg["ssl-keyfile"]));
var cert = fs.readFileSync(path.resolve(__dirname, "..",
self.cfg["ssl-certfile"]));
var options = {
key: key,
2013-09-10 19:10:30 +00:00
passphrase: self.cfg["ssl-passphrase"],
2013-09-09 22:16:41 +00:00
cert: cert
};
self.sslserv = https.createServer(options, self.app)
.listen(self.cfg["ssl-port"]);
self.sslio = require("socket.io").listen(self.sslserv);
self.sslio.set("log level", 1);
self.sslio.sockets.on("connection", function (socket) {
self.handleIOConnection(socket);
});
}
self.httpserv = self.app.listen(Server.cfg["web-port"],
2013-07-28 15:49:29 +00:00
Server.cfg["express-host"]);
self.ioserv = express().listen(Server.cfg["io-port"],
2013-09-06 21:30:20 +00:00
Server.cfg["io-host"]);
2013-07-15 22:57:33 +00:00
// init socket.io
self.io = require("socket.io").listen(self.ioserv);
self.io.set("log level", 1);
self.io.sockets.on("connection", function (socket) {
2013-09-09 22:16:41 +00:00
self.handleIOConnection(socket);
});
2013-07-16 03:01:12 +00:00
// init ACP
self.acp = require("./acp")(self);
2013-07-16 18:57:34 +00:00
// init background tasks
require("./bgtask")(self);
2013-08-03 14:52:24 +00:00
// init media retriever
self.infogetter = require("./get-info")(self);
2013-07-15 22:57:33 +00:00
},
shutdown: function () {
Logger.syslog.log("Unloading channels");
for(var i in this.channels) {
2013-07-27 14:13:30 +00:00
if(this.channels[i].registered) {
Logger.syslog.log("Saving /r/" + this.channels[i].name);
2013-07-15 22:57:33 +00:00
this.channels[i].saveDump();
2013-07-27 14:13:30 +00:00
}
2013-07-15 22:57:33 +00:00
}
Logger.syslog.log("Goodbye");
process.exit(0);
}
};
2013-07-15 22:57:33 +00:00
Logger.syslog.log("Starting CyTube v" + VERSION);
2013-07-28 23:47:13 +00:00
var chanlogpath = path.join(__dirname, "../chanlogs");
fs.exists(chanlogpath, function (exists) {
exists || fs.mkdir(chanlogpath);
2013-07-28 23:47:13 +00:00
});
var chandumppath = path.join(__dirname, "../chandump");
fs.exists(chandumppath, function (exists) {
exists || fs.mkdir(chandumppath);
2013-07-28 23:47:13 +00:00
});
Config.load(Server, path.join(__dirname, "../cfg.json"), function () {
2013-07-28 15:49:29 +00:00
Server.init();
if(!Server.cfg["debug"]) {
process.on("uncaughtException", function (err) {
Logger.errlog.log("[SEVERE] Uncaught Exception: " + err);
Logger.errlog.log(err.stack);
});
2013-07-28 15:49:29 +00:00
process.on("SIGINT", function () {
Server.shutdown();
});
}
});