171 lines
5.6 KiB
JavaScript
171 lines
5.6 KiB
JavaScript
/*Canopy - The next generation of stoner streaming software
|
|
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as
|
|
published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
|
|
|
//NPM Imports
|
|
const validator = require('validator');//No express here, so regular validator it is!
|
|
|
|
//local includes
|
|
const loggerUtils = require("../../utils/loggerUtils");
|
|
const socketUtils = require("../../utils/socketUtils");
|
|
const message = require("./message");
|
|
|
|
/**
|
|
* Class containg global server-side private message relay logic
|
|
*/
|
|
class pmHandler{
|
|
/**
|
|
* Instantiates object containing global server-side private message relay logic
|
|
* @param {Socket.io} io - Socket.io server instanced passed down from server.js
|
|
*/
|
|
constructor(io){
|
|
/**
|
|
* Socket.io server instance passed down from server.js
|
|
*/
|
|
this.io = io;
|
|
|
|
/**
|
|
* Socket.io server namespace for handling messaging
|
|
*/
|
|
this.namespace = io.of('/pm');
|
|
|
|
//Handle connections from private messaging namespace
|
|
this.namespace.on("connection", this.handleConnection.bind(this) );
|
|
}
|
|
|
|
/**
|
|
* Handles global server-side initialization for new connections to the private messaging system
|
|
* @param {Socket} socket - Requesting Socket
|
|
*/
|
|
async handleConnection(socket){
|
|
try{
|
|
//ensure unbanned ip and valid CSRF token
|
|
if(!(await socketUtils.validateSocket(socket))){
|
|
socket.disconnect();
|
|
return;
|
|
}
|
|
|
|
//If the socket wasn't authorized
|
|
if(await socketUtils.authSocketLite(socket) == null){
|
|
socket.disconnect();
|
|
return;
|
|
}
|
|
|
|
//Throw socket into room named after it's user
|
|
socket.join(socket.user.user);
|
|
|
|
//Define network related event listeners against socket
|
|
this.defineListeners(socket);
|
|
}catch(err){
|
|
//Flip a table if something fucks up
|
|
return loggerUtils.socketCriticalExceptionHandler(socket, err);
|
|
}
|
|
}
|
|
|
|
defineListeners(socket){
|
|
socket.on("pm", (data)=>{this.handlePM(data, socket)});
|
|
}
|
|
|
|
async handlePM(data, socket){
|
|
try{
|
|
//Create empty list of recipients
|
|
let recipients = [];
|
|
|
|
//For each requested recipient
|
|
for(let user of data.recipients){
|
|
//If the given user is online and didn't send the message
|
|
if(this.checkPresence(user) && user != socket.user.user){
|
|
//Add the recipient to the list
|
|
recipients.push(user);
|
|
}
|
|
}
|
|
|
|
//If we don't have any valid recipients
|
|
if(recipients.length <= 0){
|
|
//Drop that shit
|
|
return;
|
|
}
|
|
|
|
//Sanatize Message
|
|
const msg = this.sanatizeMessage(data.msg);
|
|
|
|
//If we have an invalid message
|
|
if(msg == null){
|
|
//Drop that shit
|
|
return;
|
|
}
|
|
|
|
//Create message object and relay it off to the recipients
|
|
this.relayPMObj(new message(
|
|
socket.user.user,
|
|
recipients,
|
|
msg,
|
|
[]
|
|
));
|
|
|
|
//If something fucked up
|
|
}catch(err){
|
|
//Bitch and moan
|
|
return loggerUtils.socketExceptionHandler(socket, err);
|
|
}
|
|
}
|
|
|
|
relayPMObj(msg){
|
|
//For each recipient
|
|
for(let user of msg.recipients){
|
|
//Send the message
|
|
this.namespace.to(user).emit("message", msg);
|
|
}
|
|
|
|
//Acknowledge the sent message
|
|
this.namespace.to(msg.sender).emit("sent", msg);
|
|
}
|
|
|
|
/**
|
|
* Basic function for checking presence
|
|
* This could be done using Channel Presence, but running off of bare Socket.io functionality makes this easier to implement outside the channel if need be
|
|
* @param {String} user - Username to check presence of
|
|
* @returns {Boolean} Whether or not the user is currently able to accept messages
|
|
*/
|
|
checkPresence(user){
|
|
//Pull room map from the guts of socket.io and run a null check against the given username
|
|
return this.namespace.adapter.rooms.get(user) != null;
|
|
}
|
|
|
|
/**
|
|
* Sanatizes and Validates a single message, Temporary until we get commandPreprocessor split up.
|
|
* @param {String} msg - message to validate/sanatize
|
|
* @returns {String} sanatized/validates message, returns null on validation failure
|
|
*/
|
|
sanatizeMessage(msg){
|
|
//Normally I'd kill empty messages here
|
|
//But instead we're allowing them for sesh startups
|
|
|
|
//Trim and Sanatize for XSS
|
|
msg = validator.trim(validator.escape(msg));
|
|
|
|
//Return whether or not the shit was too long
|
|
if(validator.isLength(msg, {min: 0, max: 255})){
|
|
//If it's valid return the message
|
|
return msg;
|
|
}
|
|
|
|
//if not return nothing
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = pmHandler; |