diff --git a/zefie_wtvp_minisrv/includes/classes/WTVIRC.js b/zefie_wtvp_minisrv/includes/classes/WTVIRC.js index a1ec3661..8f434742 100644 --- a/zefie_wtvp_minisrv/includes/classes/WTVIRC.js +++ b/zefie_wtvp_minisrv/includes/classes/WTVIRC.js @@ -374,7 +374,7 @@ class WTVIRC { socket.write(`:${this.serverId} UID ${nickname} 1 ${signonTime} +${userModes} ${username} ${sock.host} ${sock.realhost} ${sock.remoteAddress} ${uniqueId} * :${sock.userinfo}\r\n`); } for (const [channel, users] of this.channels.entries()) { - const modes = this.channelmodes.get(channel) || []; + const modes = this.getChannelModes(channel); for (const user of users) { let userPrefix = ''; if ((this.channelops.get(channel) || new Set()).has(user)) { @@ -870,8 +870,8 @@ class WTVIRC { } // It's a channel, broadcast to all users in the channel if (this.channels.has(targetChannel)) { - var username = this.usernames.get(nickname) || nickname; - var hostname = socket.serverinfo.name; + var username = this.usernames.get(nickname); + var hostname = this.hostnames.get(nickname); var modes = parts[3]; // Split modes into array and process each character @@ -903,18 +903,23 @@ class WTVIRC { flags.push(plusminus + mc); params++; } else { - var channelmodes = this.channelmodes.get(targetChannel); - if (!channelmodes || channelmodes === true) { - channelmodes = []; - } + this.setChannelMode(targetChannel, mc, addingFlag); if (addingFlag) { - if (!channelmodes.includes(mc)) { - channelmodes.push(mc); - } - } else { - channelmodes = channelmodes.filter(mode => mode !== mc); + if (mc === 'S' && this.kick_insecure_users_on_secure) { + // Kick users who do not have user mode +z + const usersInChannel = this.channels.get(targetChannel) || new Set(); + for (const user of usersInChannel) { + const userModes = this.usermodes.get(user) || []; + const userSocket = Array.from(this.nicknames.keys()).find(s => this.nicknames.get(s) === user); + if (userSocket && !userModes.includes('z')) { + userSocket.write(`:${nickname}!${username}@${socket.host} KICK ${targetChannel} ${userSocket.nickname} :Channel is now +S (SSL-only, +z usermode required)\r\n`); + this.broadcastChannel(targetChannel, `:${nickname}!${username}@${socket.host} KICK ${targetChannel} ${userSocket.nickname} :Channel is now +S (SSL-only, +z usermode required)\r\n`, userSocket); + this.broadcastToAllServers(`:${sourceUniqueId} KICK ${targetChannel} ${userSocket.uniqueId} :Channel is now +S (SSL-only, +z usermode required)\r\n`); + this.channels.get(targetChannel).delete(user); + } + } + } } - this.channelmodes.set(targetChannel, channelmodes); } if (modeStr.length > 0) { @@ -1005,50 +1010,23 @@ class WTVIRC { this.channelinvites.set(targetChannel, channelInvites); } else if (flags[i] === '+l' || flags[i] === '-l') { // Check if 'l' mode is already present, if not, add it with the limit - let chan_modes = this.channelmodes.get(targetChannel) || []; - if (chan_modes === true) { - chan_modes = []; - } // Check if 'l' mode is already present if (flags[i] === '+l') { - if (!chan_modes.includes('l')) { - chan_modes.push('l'); - this.channelmodes.set(targetChannel, chan_modes); - this.channellimits.set(targetChannel, target); - } + this.setChannelMode(targetChannel, 'l', true); + this.channellimits.set(targetChannel, parseInt(target)); } else { - chan_modes = chan_modes.filter(mode => mode !== 'l'); + this.setChannelMode(targetChannel, 'l', false); this.channellimits.delete(targetChannel); } } else if (flags[i] === '+k' || flags[i] === '-k') { - let chan_modes = this.channelmodes.get(targetChannel) || []; - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } if (flags[i] === '+k') { - if (!chan_modes.includes('k')) { - chan_modes.push('k'); - this.channelkeys.set(targetChannel, target); - } + this.setChannelMode(targetChannel, 'k', true); + this.channelkeys.set(targetChannel, target); } else { - chan_modes = chan_modes.filter(mode => mode !== 'k'); + this.setChannelMode(targetChannel, 'k', false); this.channelkeys.delete(targetChannel); } - this.channelmodes.set(targetChannel, chan_modes); - } else if (flags[i] === '+S' && this.kick_insecure_users_on_secure) { - // Kick users who do not have user mode +z - const usersInChannel = this.channels.get(targetChannel) || new Set(); - for (const user of usersInChannel) { - const userModes = this.usermodes.get(user) || []; - const userSocket = Array.from(this.nicknames.keys()).find(s => this.nicknames.get(s) === user); - if (userSocket && !userModes.includes('z')) { - userSocket.write(`:${nickname}!${username}@${socket.host} KICK ${targetChannel} ${userSocket.nickname} :Channel is now +S (SSL-only, +z usermode required)\r\n`); - this.broadcastChannel(targetChannel, `:${nickname}!${username}@${socket.host} KICK ${targetChannel} ${userSocket.nickname} :Channel is now +S (SSL-only, +z usermode required)\r\n`, userSocket); - this.broadcastToAllServers(`:${sourceUniqueId} KICK ${targetChannel} ${userSocket.uniqueId} :Channel is now +S (SSL-only, +z usermode required)\r\n`); - this.channels.get(targetChannel).delete(user); - } - } - } + } paramIndex++; } } @@ -1196,7 +1174,7 @@ class WTVIRC { let prefix = ''; var chanops = this.channelops.get(ch) || new Set(); var chanvoices = this.channelvoices.get(ch) || new Set(); - const modes = this.channelmodes.get(ch) || []; + const modes = this.getChannelModes(ch); if ((modes.includes('p') || modes.includes('s')) && (!this.channels.has(ch) || !this.channels.get(ch).has(socket.nickname))) { continue; // Skip listing this channel if it's private/secret and user is not in it } @@ -1250,10 +1228,7 @@ class WTVIRC { } this.broadcastChannelJoin(channelName, targetSocket); //this.broadcastToAllServers(`:${sourceUniqueId} SVSJOIN ${channelName} ${targetUniqueId}\r\n`, socket); - var chan_modes = this.channelmodes.get(channelName) || []; - if (chan_modes === true) { - chan_modes = []; - } + var chan_modes = this.getChannelModes(channelName); let modeString = ''; let modeParams = []; for (const m of chan_modes) { @@ -1459,7 +1434,7 @@ class WTVIRC { break; } // Check if channel mode +Q (no kicks) is set - var chan_modes = this.channelmodes.get(channel) || []; + var chan_modes = this.getChannelModes(channel); if (chan_modes.includes('Q')) { socket.write(`:${this.servername} 482 ${socket.nickname} ${channel} :Cannot kick users, channel is +Q (no kicks allowed)\r\n`); break; @@ -1495,10 +1470,7 @@ class WTVIRC { socket.write(`:${this.servername} 403 ${socket.nickname} ${params[0]} :No such channel\r\n`); break; } - chan_modes = this.channelmodes.get(channel) - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } + var chan_modes = this.getChannelModes(channel); if (chan_modes.includes('t')) { if (this.channelops.has(channel) && this.channelops.get(channel) instanceof Set && this.channelops.get(channel).has(socket.nickname)) { // Allow topic @@ -1779,10 +1751,7 @@ class WTVIRC { socket.write(`:${this.servername} 403 ${socket.nickname} ${channel} :No such channel\r\n`); break; } - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } + var chan_modes = this.getChannelModes(channel); chan_modes = chan_modes.map(mode => { if (typeof mode === 'string' && !mode.startsWith('+')) { @@ -1793,8 +1762,8 @@ class WTVIRC { if (chan_modes.length > 0) { var params2 = []; // Batch all modes into a single 324 reply - var modeString = chan_modes - .map(m => { + var modeString = + chan_modes.map(m => { // For modes with parameters (like k or l) if (typeof m === 'string' && (m === '+k' || m === '+l')) { if (m === '+l') { @@ -1865,7 +1834,7 @@ class WTVIRC { let inNoNickChangeChannel = false; for (const [ch, users] of this.channels.entries()) { if (users.has(socket.nickname)) { - const chanModes = this.channelmodes.get(ch) || []; + const chanModes = this.getChannelModes(ch); if (chanModes.includes('N')) { inNoNickChangeChannel = true; break; @@ -1998,68 +1967,66 @@ class WTVIRC { continue; // Skip joining this channel } } - if (this.channelmodes.has(ch)) { - // Check if the user is in too many channels - if (this.channelmodes.has(ch) && this.channelmodes.get(ch).includes('k')) { - const channelKey = this.channelkeys.get(ch); - // The key must be provided as the second parameter in the JOIN command - // params[1] is the key for the first channel, params[2] for the second, etc. - // For simplicity, assume only one channel per JOIN or the key is always params[1] - const providedKey = params[1]; - if (!providedKey || providedKey !== channelKey) { - socket.write(`:${this.servername} 475 ${socket.nickname} ${ch} :Cannot join channel (+k)\r\n`); - continue; // Skip joining this channel + // Check if the user is in too many channels + if (this.getChannelModes(ch).includes('k')) { + const channelKey = this.channelkeys.get(ch); + // The key must be provided as the second parameter in the JOIN command + // params[1] is the key for the first channel, params[2] for the second, etc. + // For simplicity, assume only one channel per JOIN or the key is always params[1] + const providedKey = params[1]; + if (!providedKey || providedKey !== channelKey) { + socket.write(`:${this.servername} 475 ${socket.nickname} ${ch} :Cannot join channel (+k)\r\n`); + continue; // Skip joining this channel + } + } + if (this.getChannelModes(ch).includes('l')) { + // Channel has a user limit (+l) + const limit = this.channellimits.get(ch) || null; + if (limit !== null && this.channels.get(ch).size >= limit) { + socket.write(`:${this.servername} 471 ${socket.nickname} ${ch} :Cannot join channel (+l)\r\n`); + continue; // Skip joining this channel + } + } + if (this.getChannelModes(ch).includes('i')) { + // Channel is invite-only (+i) + // For simplicity, let's assume you have an invited list per channel (not implemented yet) + // We'll use a Map: this.channelinvites = new Map(); // channel -> Set of invited nicks + if (!this.channelinvites) this.channelinvites = new Map(); + const invited = this.channelinvites.get(ch) || new Set(); + let isInvited = false; + for (const inviteMask of invited) { + isInvited = checkMask(inviteMask, socket); + if (isInvited) { + break; // Stop checking if we found a match } } - if (this.channelmodes.has(ch) && this.channelmodes.get(ch).includes('l')) { - // Channel has a user limit (+l) - const limit = this.channellimits.get(ch) || null; - if (limit !== null && this.channels.get(ch).size >= limit) { - socket.write(`:${this.servername} 471 ${socket.nickname} ${ch} :Cannot join channel (+l)\r\n`); - continue; // Skip joining this channel - } + if (!invited.has(socket.nickname) && !isInvited) { + socket.write(`:${this.servername} 473 ${socket.nickname} ${ch} :Cannot join channel (+i)\r\n`); + continue; // Skip joining this channel } - if (this.channelmodes.has(ch) && this.channelmodes.get(ch).includes('i')) { - // Channel is invite-only (+i) - // For simplicity, let's assume you have an invited list per channel (not implemented yet) - // We'll use a Map: this.channelinvites = new Map(); // channel -> Set of invited nicks - if (!this.channelinvites) this.channelinvites = new Map(); - const invited = this.channelinvites.get(ch) || new Set(); - let isInvited = false; - for (const inviteMask of invited) { - isInvited = checkMask(inviteMask, socket); - if (isInvited) { - break; // Stop checking if we found a match - } - } - if (!invited.has(socket.nickname) && !isInvited) { - socket.write(`:${this.servername} 473 ${socket.nickname} ${ch} :Cannot join channel (+i)\r\n`); - continue; // Skip joining this channel - } - if (!isInvited) { - invited.delete(socket.nickname); - } - this.channelinvites.set(ch, invited); + if (!isInvited) { + invited.delete(socket.nickname); } - if (this.channelmodes.has(ch) && this.channelmodes.get(ch).includes('O')) { - if (!this.isIRCOp(socket.nickname)) { - socket.write(`:${this.servername} 404 ${socket.nickname} ${ch} :Cannot join channel (+O)\r\n`); - continue; // Skip joining this channel - } + this.channelinvites.set(ch, invited); + } + if (this.getChannelModes(ch).includes('O')) { + if (!this.isIRCOp(socket.nickname)) { + socket.write(`:${this.servername} 404 ${socket.nickname} ${ch} :Cannot join channel (+O)\r\n`); + continue; // Skip joining this channel } - if (this.channelmodes.has(ch) && this.channelmodes.get(ch).includes('S')) { - // Channel is restricted to users with a secure connection (+S) - if (!socket.secure) { - socket.write(`:${this.servername} 468 ${socket.nickname} ${ch} :Cannot join channel (+S)\r\n`); - continue; // Skip joining this channel - } + } + if (this.getChannelModes(ch).includes('S')) { + // Channel is restricted to users with a secure connection (+S) + if (!socket.secure) { + socket.write(`:${this.servername} 468 ${socket.nickname} ${ch} :Cannot join channel (+S)\r\n`); + continue; // Skip joining this channel } - if (this.channelmodes.has(ch) && this.channelmodes.get(ch).includes('R')) { - // Channel is registered users only (+R) - if (!this.usermodes.get(socket.nickname).includes('r')) { - socket.write(`:${this.servername} 447 ${socket.nickname} ${ch} :Cannot join channel (+R)\r\n`); - continue; // Skip joining this channel - } + } + if (this.getChannelModes(ch).includes('R')) { + // Channel is registered users only (+R) + if (!this.usermodes.get(socket.nickname).includes('r')) { + socket.write(`:${this.servername} 447 ${socket.nickname} ${ch} :Cannot join channel (+R)\r\n`); + continue; // Skip joining this channel } } @@ -2078,7 +2045,7 @@ class WTVIRC { this.channelops.get(ch).add(socket.nickname); } this.broadcastChannelJoin(ch, socket); - let modes = this.channelmodes.get(ch) || []; + let modes = this.getChannelModes(ch); let prefix = ''; if ((this.channelops.get(ch) || new Set()).has(socket.nickname)) { if (socket.client_caps.includes('multi-prefix')) { @@ -2269,7 +2236,7 @@ class WTVIRC { break; } if (!this.channels.has(channel) || !this.channels.get(channel).has(invitee)) { - if (this.channelmodes.has(channel) && this.channelmodes.get(channel).includes('V')) { + if (this.getChannelModes(channel).includes('V')) { socket.write(`:${this.servername} 482 ${socket.nickname} ${channel} :Cannot invite users, channel is +V (no invites allowed)\r\n`); break; } @@ -2302,10 +2269,7 @@ class WTVIRC { continue; // Skip invalid channel names } if (this.channelmodes.has(channel)) { - var modes = this.channelmodes.get(channel); - if (modes === true) { - modes = []; - } + var modes = this.getChannelModes(channel); if (modes.includes('p')) { if (!this.channels.has(channel) || !this.channels.get(channel).has(socket.nickname)) { continue; // Skip if user is not in the channel @@ -2348,7 +2312,7 @@ class WTVIRC { if (isChannel) { // WHO for channel if (this.channelmodes.has(target)) { - const modes = this.channelmodes.get(target); + const modes = this.getChannelModes(target); if ((modes.includes('p') || modes.includes('s')) && (!this.channels.has(target) || !this.channels.get(target).has(socket.nickname))) { socket.write(`:${this.servername} 315 ${socket.nickname} ${target} :End of /WHO list\r\n`); break; @@ -2474,7 +2438,7 @@ class WTVIRC { var msg = line.slice(line.indexOf(':', 1) + 1); if (isChan) { // Channel message - if (this.channelmodes.has(t) && this.channelmodes.get(t).includes('m')) { + if (this.getChannelModes(t).includes('m')) { // Channel is moderated (+m) var voices = this.channelvoices.get(t) || new Set(); var ops = this.channelops.get(t) || new Set(); @@ -2485,28 +2449,28 @@ class WTVIRC { continue; } } - if (this.channelmodes.has(t) && this.channelmodes.get(t).includes('n')) { + if (this.getChannelModes(t).includes('n')) { // Channel is no-external-messages (+n) if (!this.channels.has(t) || !this.channels.get(t).has(socket.nickname)) { socket.write(`:${this.servername} 404 ${socket.nickname} ${t} :Cannot send to channel (+n)\r\n`); continue; } } - if (this.channelmodes.has(t) && this.channelmodes.get(t).includes('c')) { + if (this.getChannelModes(t).includes('c')) { // Block all IRC control codes (ASCII 0x00-0x1F except \r and \n) if (/[\x00-\x09\x0B\x0C\x0E-\x1F]/.test(msg)) { socket.write(`:${this.servername} 404 ${socket.nickname} ${t} :Cannot send to channel (+c)\r\n`); continue; } } - if (this.channelmodes.has(t) && this.channelmodes.get(t).includes('C')) { + if (this.getChannelModes(t).includes('C')) { // channel blocks CTCP, detect if the message contains CTCPS if (msg.includes('\x01')) { socket.write(`:${this.servername} 404 ${socket.nickname} ${t} :Cannot send to channel (+C)\r\n`); continue; } } - if (this.channelmodes.has(t) && this.channelmodes.get(t).includes('O')) { + if (this.getChannelModes(t).includes('O')) { if (!this.isIRCOp(socket.nickname)) { socket.write(`:${this.servername} 404 ${socket.nickname} ${t} :Cannot send to channel (+O)\r\n`); } @@ -2607,28 +2571,28 @@ class WTVIRC { var msg = line.slice(line.indexOf(':', 1) + 1); if (isChan) { // Channel notice - if (this.channelmodes.has(t) && this.channelmodes.get(t).includes('n')) { + if (this.getChannelModes(t).includes('n')) { // Channel is no-external-messages (+n) if (!this.channels.has(t) || !this.channels.get(t).has(socket.nickname)) { socket.write(`:${this.servername} 404 ${socket.nickname} ${t} :Cannot send to channel (+n)\r\n`); continue; } } - if (this.channelmodes.has(t) && this.channelmodes.get(t).includes('c')) { + if (this.getChannelModes(t).includes('c')) { // channel blocks color, detect if the message contains color codes if (/[\x00-\x09\x0B\x0C\x0E-\x1F]/.test(msg)) { socket.write(`:${this.servername} 404 ${socket.nickname} ${t} :Cannot send to channel (+c)\r\n`); continue; } } - if (this.channelmodes.has(t) && this.channelmodes.get(t).includes('C')) { + if (this.getChannelModes(t).includes('C')) { // channel blocks CTCP, detect if the message contains CTCPS if (msg.includes('\x01')) { socket.write(`:${this.servername} 404 ${socket.nickname} ${t} :Cannot send to channel (+C)\r\n`); continue; } } - if (this.channelmodes.has(t) && this.channelmodes.get(t).includes('T')) { + if (this.getChannelModes(t).includes('T')) { const ops = this.channelops.get(t) || new Set(); const halfops = this.channelhalfops.get(t) || new Set(); const voices = this.channelvoices.get(t) || new Set(); @@ -2641,7 +2605,7 @@ class WTVIRC { continue; } } - if (this.channelmodes.has(t) && this.channelmodes.get(t).includes('O')) { + if (this.getChannelModes(t).includes('O')) { if (!this.isIRCOp(socket.nickname)) { socket.write(`:${this.servername} 404 ${socket.nickname} ${t} :Cannot send to channel (+O)\r\n`); } @@ -2726,7 +2690,8 @@ class WTVIRC { var chanops = this.channelops.get(ch) || new Set(); var chanhalfops = this.channelhalfops.get(ch) || new Set(); var chanvoices = this.channelvoices.get(ch) || new Set(); - const modes = this.channelmodes.get(ch) || []; + var modes = this.getChannelModes(ch); + if ((modes.includes('p') || modes.includes('s')) && (!this.channels.has(ch) || !this.channels.get(ch).has(socket.nickname))) { continue; // Skip listing this channel if it's private/secret and user is not in it } @@ -3485,24 +3450,18 @@ class WTVIRC { const socket = Array.from(this.nicknames.keys()).find(s => this.nicknames.get(s) === nickname); const username = this.usernames.get(nickname); if (mode.startsWith('+m')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (!chan_modes.includes('m')) { - this.channelmodes.set(channel, [...chan_modes, 'm']); + if (!this.getChannelModes(channel).includes('m')) { + this.setChannelMode(channel, 'm', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +m\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +m\r\n`); } return; } else if (mode.startsWith('-m')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + if (this.getChannelModes(channel).includes('m')) { + this.setChannelMode(channel, 'm', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -m\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -m\r\n`); } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'm')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -m\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -m\r\n`); return; } else if (mode.startsWith("+I")) { if (params.length < 3) { @@ -3577,13 +3536,9 @@ class WTVIRC { socket.write(`:${this.servername} 501 ${nickname} :Invalid channel limit\r\n`); return; } - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } this.channellimits.set(channel, limit); - if (!chan_modes.includes('l')) { - this.channelmodes.set(channel, [...chan_modes, 'l']); + if (!this.getChannelModes(channel).includes('l')) { + this.setChannelMode(channel, 'l', true); } this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +l ${limit}\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +l ${limit}\r\n`); @@ -3593,14 +3548,14 @@ class WTVIRC { socket.write(`:${this.servername} 461 ${nickname} MODE :Not enough parameters\r\n`); return; } - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + if (this.channellimits.has(channel)) { + this.channellimits.delete(channel); + } + if (!this.getChannelModes(channel).includes('l')) { + this.setChannelMode(channel, 'l', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -l\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -l\r\n`); } - this.channellimits.delete(channel); - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'l')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -l\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -l\r\n`); return; } else if (mode.startsWith('+k')) { if (params.length < 3) { @@ -3612,47 +3567,37 @@ class WTVIRC { socket.write(`:${this.servername} 525 ${nickname} :Invalid channel key\r\n`); return; } - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } // replace key mode if it exists this.channelkeys.set(channel, key); - if (!chan_modes.includes('k')) { - this.channelmodes.set(channel, [...chan_modes, 'k']); + if (!this.getChannelModes(channel).includes('k')) { + this.setChannelMode(channel, 'k', true); } this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +k ${key}\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +k ${key}\r\n`); return; - } else if (mode.startsWith('-k')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + } else if (mode.startsWith('-k')) { + if (this.channelkeys.has(channel)) { + this.channelkeys.delete(channel); + } + if (!this.getChannelModes(channel).includes('k')) { + this.setChannelMode(channel, 'k', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -k\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -k\r\n`); } - this.channelkeys.delete(channel); - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'k')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -k\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -k\r\n`); return; - } else if (mode.startsWith('+i')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (!chan_modes.includes('i')) { - this.channelmodes.set(channel, [...chan_modes, 'i']); + } else if (mode.startsWith('+i')) { + if (!this.getChannelModes(channel).includes('i')) { + this.setChannelMode(channel, 'i', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +i\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +i\r\n`); } return; - } else if (mode.startsWith('-i')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + } else if (mode.startsWith('-i')) { + if (!this.getChannelModes(channel).includes('i')) { + this.setChannelMode(channel, 'i', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -i\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -i\r\n`); } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'i')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -i\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -i\r\n`); return; } else if (mode.startsWith('+o')) { if (params.length < 3) { @@ -3823,245 +3768,175 @@ class WTVIRC { } return; } else if (mode.startsWith("+n")) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (!chan_modes.includes('n')) { - this.channelmodes.set(channel, [...chan_modes, 'n']); + if (!this.getChannelModes(channel).includes('n')) { + this.setChannelMode(channel, 'n', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +n\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +n\r\n`); } return; } else if (mode.startsWith("-n")) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + if (!this.getChannelModes(channel).includes('n')) { + this.setChannelMode(channel, 'n', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -n\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -n\r\n`); } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'n')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -n\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -n\r\n`); return; } else if (mode.startsWith('+s')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (!chan_modes.includes('s')) { - this.channelmodes.set(channel, [...chan_modes, 's']); + if (!this.getChannelModes(channel).includes('s')) { + this.setChannelMode(channel, 's', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +s\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +s\r\n`); } return; } else if (mode.startsWith('-s')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + if (!this.getChannelModes(channel).includes('s')) { + this.setChannelMode(channel, 's', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -s\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -s\r\n`); } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 's')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -s\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -s\r\n`); return; } else if (mode.startsWith('+p')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (!chan_modes.includes('p')) { - this.channelmodes.set(channel, [...chan_modes, 'p']); + if (!this.getChannelModes(channel).includes('p')) { + this.setChannelMode(channel, 'p', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +p\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +p\r\n`); } return; } else if (mode.startsWith('-p')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + if (!this.getChannelModes(channel).includes('p')) { + this.setChannelMode(channel, 'p', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -p\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -p\r\n`); } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'p')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -p\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -p\r\n`); return; } else if (mode.startsWith('+T')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (!chan_modes.includes('T')) { - this.channelmodes.set(channel, [...chan_modes, 'T']); + if (!this.getChannelModes(channel).includes('T')) { + this.setChannelMode(channel, 'T', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +T\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +T\r\n`); } return; } else if (mode.startsWith('-T')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + if (!this.getChannelModes(channel).includes('T')) { + this.setChannelMode(channel, 'T', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -T\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -T\r\n`); } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'T')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -T\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -T\r\n`); return; } else if (mode.startsWith('+V')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (!chan_modes.includes('V')) { - this.channelmodes.set(channel, [...chan_modes, 'V']); + if (!this.getChannelModes(channel).includes('V')) { + this.setChannelMode(channel, 'V', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +V\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +V\r\n`); } return; } else if (mode.startsWith('-V')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + if (!this.getChannelModes(channel).includes('V')) { + this.setChannelMode(channel, 'V', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -V\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -V\r\n`); } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'V')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -V\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -V\r\n`); return; } else if (mode.startsWith('+c')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (!chan_modes.includes('c')) { - this.channelmodes.set(channel, [...chan_modes, 'c']); + if (!this.getChannelModes(channel).includes('c')) { + this.setChannelMode(channel, 'c', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +c\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +c\r\n`); } return; } else if (mode.startsWith('-c')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + if (!this.getChannelModes(channel).includes('c')) { + this.setChannelMode(channel, 'c', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -c\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -c\r\n`); } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'c')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -c\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -c\r\n`); return; } else if (mode.startsWith('+C')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (!chan_modes.includes('C')) { - this.channelmodes.set(channel, [...chan_modes, 'C']); + if (!this.getChannelModes(channel).includes('C')) { + this.setChannelMode(channel, 'C', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +C\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +C\r\n`); } return; } else if (mode.startsWith('-C')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + if (!this.getChannelModes(channel).includes('C')) { + this.setChannelMode(channel, 'C', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -C\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -C\r\n`); } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'C')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -C\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -C\r\n`); return; } else if (mode.startsWith('+R')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (!chan_modes.includes('R')) { - this.channelmodes.set(channel, [...chan_modes, 'R']); + if (!this.getChannelModes(channel).includes('R')) { + this.setChannelMode(channel, 'R', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +R\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +R\r\n`); } return; } else if (mode.startsWith('-R')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + if (!this.getChannelModes(channel).includes('R')) { + this.setChannelMode(channel, 'R', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -R\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -R\r\n`); } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'R')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -R\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -R\r\n`); return; } else if (mode.startsWith('+N')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (!chan_modes.includes('N')) { - this.channelmodes.set(channel, [...chan_modes, 'N']); + if (!this.getChannelModes(channel).includes('N')) { + this.setChannelMode(channel, 'N', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +N\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +N\r\n`); } return; } else if (mode.startsWith('-N')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + if (!this.getChannelModes(channel).includes('N')) { + this.setChannelMode(channel, 'N', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -N\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -N\r\n`); } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'N')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -N\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -N\r\n`); return; } else if (mode.startsWith('+Q')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (!chan_modes.includes('Q')) { - this.channelmodes.set(channel, [...chan_modes, 'Q']); + if (!this.getChannelModes(channel).includes('Q')) { + this.setChannelMode(channel, 'Q', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +Q\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +Q\r\n`); } return; } else if (mode.startsWith('-Q')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + if (!this.getChannelModes(channel).includes('Q')) { + this.setChannelMode(channel, 'Q', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -Q\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -Q\r\n`); } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'Q')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -Q\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -Q\r\n`); return; } - else if (mode.startsWith('+O')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } + else if (mode.startsWith('+O')) { if (!this.isIRCOp(nickname)) { socket.write(`:${this.servername} ${nickname} ${channel} :You're not an IRC operator\r\n`); return; } - if (!chan_modes.includes('O')) { - this.channelmodes.set(channel, [...chan_modes, 'O']); + if (!this.getChannelModes(channel).includes('O')) { + this.setChannelMode(channel, 'O', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +O\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +O\r\n`); } return; } else if (mode.startsWith('-O')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } if (!this.isIRCOp(nickname)) { socket.write(`:${this.servername} 482 ${nickname} ${channel} :You're not an IRC operator\r\n`); return; } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'O')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -O\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -O\r\n`); + if (!this.getChannelModes(channel).includes('O')) { + this.setChannelMode(channel, 'O', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -O\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -O\r\n`); + } return; } else if (mode.startsWith('+S')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } if (!socket.secure) { socket.write(`:${this.servername} 484 ${nickname} ${channel} :You must be connected via SSL/TLS to set +S\r\n`); return; } - if (!chan_modes.includes('S')) { - this.channelmodes.set(channel, [...chan_modes, 'S']); + if (!this.getChannelModes(channel).includes('S')) { + this.setChannelMode(channel, 'S', true); if (this.kick_insecure_users_on_secure) { const usersInChannel = this.channels.get(channel) || new Set(); for (const user of usersInChannel) { @@ -4079,35 +3954,25 @@ class WTVIRC { } return; } else if (mode.startsWith('-S')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (chan_modes.includes('S')) { - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 'S')); + if (this.getChannelModes(channel).includes('S')) { + this.setChannelMode(channel, 'S', false); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -S\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -S\r\n`); } return; } else if (mode.startsWith('+t')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; - } - if (!chan_modes.includes('t')) { - this.channelmodes.set(channel, [...chan_modes, 't']); + if (!this.getChannelModes(channel).includes('t')) { + this.setChannelMode(channel, 't', true); this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} +t\r\n`); this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} +t\r\n`); } return; } else if (mode.startsWith('-t')) { - var chan_modes = this.channelmodes.get(channel); - if (!chan_modes || chan_modes === true) { - chan_modes = []; + if (!this.getChannelModes(channel).includes('t')) { + this.setChannelMode(channel, 't', false); + this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -t\r\n`); + this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -t\r\n`); } - this.channelmodes.set(channel, (chan_modes).filter(m => m !== 't')); - this.broadcastChannel(channel, `:${nickname}!${username}@${socket.host} MODE ${channel} -t\r\n`); - this.broadcastToAllServers(`:${socket.uniqueId} MODE ${channel} -t\r\n`); return; } else if (mode === 'b') { if (this.channelbans.has(channel)) { @@ -4302,10 +4167,7 @@ class WTVIRC { break; } } - if (foundChannel) { - return foundChannel; - } - return null; + return foundChannel; } findUser(username) { @@ -4317,10 +4179,7 @@ class WTVIRC { break; } } - if (foundUser) { - return foundUser; - } - return null; + return foundUser; } clientIsWebTV(socket) { @@ -4358,9 +4217,36 @@ class WTVIRC { this.sendWebTVNoticeTo(socket, `*** Notice --- Client connecting: ${clientSocket.nickname} (${clientSocket.username}@${clientSocket.host}) [${clientSocket.remoteAddress}] {users} [${clientSocket.userinfo}] <${clientSocket.uniqueId}>`); } } - } + } } + getChannelModes(channel) { + const modes = Array.isArray(this.channelmodes.get(channel)) + ? [...this.channelmodes.get(channel)] + : this.channelmodes.get(channel); + if (!modes || modes === true) { + modes = this.default_channel_modes; + } + return modes; + } + + setChannelMode(channel, mode, adding) { + const modes = this.getChannelModes(channel); + if (adding) { + if (!modes.includes(mode)) { + modes.push(mode); + } + } else { + const index = modes.indexOf(mode); + if (index !== -1) { + modes.splice(index, 1); + } + } + this.channelmodes.set(channel, modes); + } + + + doLogin(nickname, socket) { for (const [srvSocket, serverName] of this.servers.entries()) { if (srvSocket) {