From 8820968c7378b03af57abce3d3a522cff318c9b9 Mon Sep 17 00:00:00 2001 From: marzavec Date: Sat, 28 Apr 2018 22:29:38 -0700 Subject: misc server changes and new modules --- server/src/commands/core/changenick.js | 90 ++++++++++++++++++++++++++++++++++ server/src/commands/core/disconnect.js | 23 +++++++++ server/src/commands/core/invite.js | 20 ++++---- server/src/commands/core/join.js | 52 +++++++++++--------- server/src/commands/core/morestats.js | 1 + server/src/commands/core/move.js | 85 ++++++++++++++++++++++++++++++++ 6 files changed, 237 insertions(+), 34 deletions(-) create mode 100644 server/src/commands/core/changenick.js create mode 100644 server/src/commands/core/disconnect.js create mode 100644 server/src/commands/core/move.js (limited to 'server/src/commands/core') diff --git a/server/src/commands/core/changenick.js b/server/src/commands/core/changenick.js new file mode 100644 index 0000000..460f811 --- /dev/null +++ b/server/src/commands/core/changenick.js @@ -0,0 +1,90 @@ +/* + Description: Generates a semi-unique channel name then broadcasts it to each client +*/ + +'use strict'; + +const verifyNickname = (nick) => { + return /^[a-zA-Z0-9_]{1,24}$/.test(nick); +}; + +exports.run = async (core, server, socket, data) => { + if (server._police.frisk(socket.remoteAddress, 6)) { + server.reply({ + cmd: 'warn', + text: 'You are changing nicknames too fast. Wait a moment before trying again.' + }, socket); + + return; + } + + if (typeof data.nick !== 'string') { + return; + } + + let newNick = data.nick.trim(); + + if (!verifyNickname(newNick)) { + server.reply({ + cmd: 'warn', + text: 'Nickname must consist of up to 24 letters, numbers, and underscores' + }, socket); + + return; + } + + if (newNick.toLowerCase() == core.config.adminName.toLowerCase()) { + server._police.frisk(socket.remoteAddress, 4); + + server.reply({ + cmd: 'warn', + text: 'Gtfo' + }, socket); + + return; + } + + let userExists = server.findSockets({ + channel: data.channel, + nick: (targetNick) => targetNick.toLowerCase() === newNick.toLowerCase() + }); + + if (userExists.length > 0) { + // That nickname is already in that channel + server.reply({ + cmd: 'warn', + text: 'Nickname taken' + }, socket); + + return; + } + + let peerList = server.findSockets({ channel: socket.channel }); + let leaveNotice = { + cmd: 'onlineRemove', + nick: socket.nick + }; + let joinNotice = { + cmd: 'onlineAdd', + nick: newNick, + trip: socket.trip || 'null', + hash: server.getSocketHash(socket) + }; + + server.broadcast( leaveNotice, { channel: socket.channel }); + server.broadcast( joinNotice, { channel: socket.channel }); + server.broadcast( { + cmd: 'info', + text: `${socket.nick} is now ${newNick}` + }, { channel: socket.channel }); + + socket.nick = newNick; +}; + +exports.requiredData = ['nick']; + +exports.info = { + name: 'changenick', + usage: 'changenick {nick}', + description: 'This will change your current connections nickname' +}; diff --git a/server/src/commands/core/disconnect.js b/server/src/commands/core/disconnect.js new file mode 100644 index 0000000..1a9c635 --- /dev/null +++ b/server/src/commands/core/disconnect.js @@ -0,0 +1,23 @@ +/* + Description: This module will be directly called by the server event handler + when a socket connection is closed or lost. It can calso be called + by a client to have the connection severed. +*/ + +'use strict'; + +exports.run = async (core, server, socket, data) => { + if (socket.channel) { + server.broadcast({ + cmd: 'onlineRemove', + nick: socket.nick + }, { channel: socket.channel }); + } + + socket.terminate(); +}; + +exports.info = { + name: 'disconnect', + description: 'Event handler or force disconnect (if your into that kind of thing)' +}; diff --git a/server/src/commands/core/invite.js b/server/src/commands/core/invite.js index a6412e1..bd85812 100644 --- a/server/src/commands/core/invite.js +++ b/server/src/commands/core/invite.js @@ -9,6 +9,15 @@ const verifyNickname = (nick) => { }; exports.run = async (core, server, socket, data) => { + if (server._police.frisk(socket.remoteAddress, 2)) { + server.reply({ + cmd: 'warn', + text: 'You are sending invites too fast. Wait a moment before trying again.' + }, socket); + + return; + } + if (typeof data.nick !== 'string') { return; } @@ -22,16 +31,7 @@ exports.run = async (core, server, socket, data) => { // They invited themself return; } - - if (server._police.frisk(socket.remoteAddress, 2)) { - server.reply({ - cmd: 'warn', - text: 'You are sending invites too fast. Wait a moment before trying again.' - }, socket); - - return; - } - + let channel = Math.random().toString(36).substr(2, 8); let payload = { diff --git a/server/src/commands/core/join.js b/server/src/commands/core/join.js index e896361..82b48d2 100644 --- a/server/src/commands/core/join.js +++ b/server/src/commands/core/join.js @@ -28,7 +28,6 @@ exports.run = async (core, server, socket, data) => { if (typeof socket.channel !== 'undefined') { // Calling socket already in a channel - // TODO: allow changing of channel without reconnection return; } @@ -56,17 +55,19 @@ exports.run = async (core, server, socket, data) => { return; } - for (let client of server.clients) { - if (client.channel === channel) { - if (client.nick.toLowerCase() === nick.toLowerCase()) { - server.reply({ - cmd: 'warn', - text: 'Nickname taken' - }, socket); + let userExists = server.findSockets({ + channel: data.channel, + nick: (targetNick) => targetNick.toLowerCase() === nick.toLowerCase() + }); - return; - } - } + if (userExists.length > 0) { + // That nickname is already in that channel + server.reply({ + cmd: 'warn', + text: 'Nickname taken' + }, socket); + + return; } // TODO: Should we check for mod status first to prevent overwriting of admin status somehow? Meh, w/e, cba. @@ -75,6 +76,8 @@ exports.run = async (core, server, socket, data) => { let password = nickArray[1]; if (nick.toLowerCase() == core.config.adminName.toLowerCase()) { if (password != core.config.adminPass) { + server._police.frisk(socket.remoteAddress, 4); + server.reply({ cmd: 'warn', text: 'Gtfo' @@ -83,7 +86,7 @@ exports.run = async (core, server, socket, data) => { return; } else { uType = 'admin'; - trip = hash(password + core.config.tripSalt); + trip = 'Admin'; } } else if (password) { trip = hash(password + core.config.tripSalt); @@ -91,30 +94,31 @@ exports.run = async (core, server, socket, data) => { // TODO: Disallow moderator impersonation for (let mod of core.config.mods) { - if (trip === mod.trip) + if (trip === mod.trip) { uType = 'mod'; + } } - // Announce the new user - server.broadcast({ + // Reply with online user list + let newPeerList = server.findSockets({ channel: data.channel }); + let joinAnnouncement = { cmd: 'onlineAdd', nick: nick, trip: trip || 'null', hash: server.getSocketHash(socket) - }, { channel: channel }); + }; + let nicks = []; + + for (let i = 0, l = newPeerList.length; i < l; i++) { + server.reply(joinAnnouncement, newPeerList[i]); + nicks.push(newPeerList[i].nick); + } socket.uType = uType; socket.nick = nick; socket.channel = channel; if (trip !== null) socket.trip = trip; - - // Reply with online user list - let nicks = []; - for (let client of server.clients) { - if (client.channel === channel) { - nicks.push(client.nick); - } - } + nicks.push(socket.nick); server.reply({ cmd: 'onlineSet', diff --git a/server/src/commands/core/morestats.js b/server/src/commands/core/morestats.js index 887d663..d8bc23d 100644 --- a/server/src/commands/core/morestats.js +++ b/server/src/commands/core/morestats.js @@ -41,6 +41,7 @@ exports.run = async (core, server, socket, data) => { invites-sent: ${(core.managers.stats.get('invites-sent') || 0)} messages-sent: ${(core.managers.stats.get('messages-sent') || 0)} users-banned: ${(core.managers.stats.get('users-banned') || 0)} + users-kicked: ${(core.managers.stats.get('users-kicked') || 0)} stats-requested: ${(core.managers.stats.get('stats-requested') || 0)} server-uptime: ${formatTime(process.hrtime(core.managers.stats.get('start-time')))}` }, socket); diff --git a/server/src/commands/core/move.js b/server/src/commands/core/move.js new file mode 100644 index 0000000..862025c --- /dev/null +++ b/server/src/commands/core/move.js @@ -0,0 +1,85 @@ +/* + Description: Generates a semi-unique channel name then broadcasts it to each client +*/ + +'use strict'; + +exports.run = async (core, server, socket, data) => { + if (server._police.frisk(socket.remoteAddress, 6)) { + server.reply({ + cmd: 'warn', + text: 'You are changing channels too fast. Wait a moment before trying again.' + }, socket); + + return; + } + + if (typeof data.channel !== 'string') { + return; + } + + if (data.channel === socket.channel) { + // They are trying to rejoin the channel + return; + } + + const currentNick = socket.nick.toLowerCase(); + let userExists = server.findSockets({ + channel: data.channel, + nick: (targetNick) => targetNick.toLowerCase() === currentNick + }); + + if (userExists.length > 0) { + // That nickname is already in that channel + return; + } + + let peerList = server.findSockets({ channel: socket.channel }); + + if (peerList.length > 1) { + for (let i = 0, l = peerList.length; i < l; i++) { + server.reply({ + cmd: 'onlineRemove', + nick: peerList[i].nick + }, socket); + + if (socket.nick !== peerList[i].nick){ + server.reply({ + cmd: 'onlineRemove', + nick: socket.nick + }, peerList[i]); + } + } + } + + let newPeerList = server.findSockets({ channel: data.channel }); + let moveAnnouncement = { + cmd: 'onlineAdd', + nick: socket.nick, + trip: socket.trip || 'null', + hash: server.getSocketHash(socket) + }; + let nicks = []; + + for (let i = 0, l = newPeerList.length; i < l; i++) { + server.reply(moveAnnouncement, newPeerList[i]); + nicks.push(newPeerList[i].nick); + } + + nicks.push(socket.nick); + + server.reply({ + cmd: 'onlineSet', + nicks: nicks + }, socket); + + socket.channel = data.channel; +}; + +exports.requiredData = ['channel']; + +exports.info = { + name: 'move', + usage: 'move {channel}', + description: 'This will change the current channel to the new one provided' +}; -- cgit v1.2.1