| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 | 
							- 'use strict';
 
- // This file contains all the logic for Socket.IO
 
- const 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();
 
- 		});
 
- 	}
 
- 	async io () {
 
- 		try { await this._validateHook(); } catch { return; }
 
- 		return this._io;
 
- 	}
 
- }
 
 
  |