| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 | 'use strict';// This file contains all the logic for Socket.IOconst coreClass = require("../core");const socketio = require("socket.io");const async = require("async");const config = require("config");module.exports = class extends coreClass {	constructor(name, moduleManager) {		super(name, moduleManager);		this.dependsOn = ["app", "db", "cache", "utils"];	}	initialize() {		return new Promise(resolve => {			this.setStage(1);			const 	logger		= this.logger,					app			= this.moduleManager.modules["app"],					cache		= this.moduleManager.modules["cache"],					utils		= this.moduleManager.modules["utils"],					db			= this.moduleManager.modules["db"],					punishments	= this.moduleManager.modules["punishments"];						const actions = require('../logic/actions');			const SIDname = config.get("cookie.SIDname");			// TODO: Check every 30s/60s, for all sockets, if they are still allowed to be in the rooms they are in, and on socket at all (permission changing/banning)			this.io = socketio(app.server);			this.io.use(async (socket, next) => {				try { await this._validateHook(); } catch { return; }				let SID;				socket.ip = socket.request.headers['x-forwarded-for'] || '0.0.0.0';				async.waterfall([					(next) => {						utils.parseCookies(							socket.request.headers.cookie						).then(res => {							SID = res[SIDname];							next(null);						});					},					(next) => {						if (!SID) return next('No SID.');						next();					},					(next) => {						cache.hget('sessions', SID, next);					},					(session, next) => {						if (!session) return next('No session found.');						session.refreshDate = Date.now();												socket.session = session;						cache.hset('sessions', SID, session, next);					},					(res, next) => {						// check if a session's user / IP is banned						punishments.getPunishments((err, punishments) => {							const isLoggedIn = !!(socket.session && socket.session.refreshDate);							const userId = (isLoggedIn) ? socket.session.userId : null;							let banishment = { banned: false, ban: 0 };							punishments.forEach(punishment => {								if (punishment.expiresAt > banishment.ban) banishment.ban = punishment;								if (punishment.type === 'banUserId' && isLoggedIn && punishment.value === userId) banishment.banned = true;								if (punishment.type === 'banUserIp' && punishment.value === socket.ip) banishment.banned = true;							});														socket.banishment = banishment;							next();						});					}				], () => {					if (!socket.session) socket.session = { socketId: socket.id };					else socket.session.socketId = socket.id;					next();				});			});			this.io.on('connection', async socket => {				try { await this._validateHook(); } catch { return; }				let sessionInfo = '';								if (socket.session.sessionId) sessionInfo = ` UserID: ${socket.session.userId}.`;				// if session is banned				if (socket.banishment && socket.banishment.banned) {					logger.info('IO_BANNED_CONNECTION', `A user tried to connect, but is currently banned. IP: ${socket.ip}.${sessionInfo}`);					socket.emit('keep.event:banned', socket.banishment.ban);					socket.disconnect(true);				} else {					logger.info('IO_CONNECTION', `User connected. IP: ${socket.ip}.${sessionInfo}`);					// catch when the socket has been disconnected					socket.on('disconnect', () => {						if (socket.session.sessionId) sessionInfo = ` UserID: ${socket.session.userId}.`;						logger.info('IO_DISCONNECTION', `User disconnected. IP: ${socket.ip}.${sessionInfo}`);					});					// catch errors on the socket (internal to socket.io)					socket.on('error', console.error);					// have the socket listen for each action					Object.keys(actions).forEach(namespace => {						Object.keys(actions[namespace]).forEach(action => {							// the full name of the action							let name = `${namespace}.${action}`;							// listen for this action to be called							socket.on(name, async (...args) => {								let cb = args[args.length - 1];								if (typeof cb !== "function")									cb = () => {										this.logger.info("IO_MODULE", `There was no callback provided for ${name}.`);									}								else args.pop();								try { await this._validateHook(); } catch { return cb({status: 'failure', message: 'Lockdown'}); } 								// load the session from the cache								cache.hget('sessions', socket.session.sessionId, (err, session) => {									if (err && err !== true) {										if (typeof cb === 'function') return cb({											status: 'error',											message: 'An error occurred while obtaining your session'										});									}									// make sure the sockets sessionId isn't set if there is no session									if (socket.session.sessionId && session === null) delete socket.session.sessionId;									// call the action, passing it the session, and the arguments socket.io passed us									actions[namespace][action].apply(null, [socket.session].concat(args).concat([										(result) => {											// respond to the socket with our message											if (typeof cb === 'function') return cb(result);										}									]));								});							})						})					});					if (socket.session.sessionId) {						cache.hget('sessions', socket.session.sessionId, (err, session) => {							if (err && err !== true) socket.emit('ready', false);							else if (session && session.userId) {								db.models.user.findOne({ _id: session.userId }, (err, user) => {									if (err || !user) return socket.emit('ready', false);									let role = '';									let username = '';									let userId = '';									if (user) {										role = user.role;										username = user.username;										userId = session.userId;									}									socket.emit('ready', true, role, username, userId);								});							} else socket.emit('ready', false);						})					} else socket.emit('ready', false);				}			});			resolve();		});	}}
 |