diff --git a/src/Channel.ts b/src/Channel.ts index b6febb9..c5e9b2e 100644 --- a/src/Channel.ts +++ b/src/Channel.ts @@ -3,6 +3,7 @@ import { MatrixUser } from "./MatrixUser.js"; import { IRCUser } from "./IRCUser.js"; import { Client } from "./Client.js"; import numerics from "./numerics.js"; +import { IRCMessage } from "./Message.js"; export class Channel { public name: string @@ -46,7 +47,9 @@ export class Channel { if (!newName || newName === this.name) return; const oldName = this.name; + this.server.ircChannels.delete(oldName); this.name = newName; + this.server.ircChannels.set(newName, this); this.ircUsers.forEach((user, username) => { user.getClients().forEach(client => { if (client.enabledCaps.has("draft/channel-rename")) { @@ -136,7 +139,11 @@ export class Channel { ] client.sendMessage(client.server.name, '352', userParams, passedTags); } - client.sendMessage(client.server.name, '315', [this.name], passedTags); + client.sendMessage(client.server.name, '315', [client.user.nick, this.name, "End of /WHO"], passedTags); + } + + handleModeChange(client: Client, message: IRCMessage) { + } routeMatrixEvent(event: any) { diff --git a/src/Client.ts b/src/Client.ts index d6da89f..a5a0528 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -49,6 +49,56 @@ export class Client { }); } + checkIfRegistered(passedTags: Map = new Map()) { + if (this.user === null) + this.sendMessage(this.server.name, "451", [this.localNick, "You have not registered"], passedTags); + + return this.user !== null; + } + + getMatrixUserFromNick(targetNick: string, passedTags: Map = new Map()) { + if (!this.user) return false; + const targetMxid = this.server.nickToMxid.get(targetNick); + if (!targetMxid) { + this.sendMessage(this.server.name, "401", [this.user.nick, targetNick, "No such nick"], passedTags); + return false; + } + const target = this.server.matrixUsers.get(targetMxid); + if (!target) { + this.sendMessage(this.server.name, "401", [this.user.nick, targetNick, "No such nick"], passedTags); + return false; + } + return target; + } + + getChannel(channel: string, passedTags: Map = new Map()) { + if (!this.user) return false; + const targetChannel = this.server.ircChannels.get(channel); + if (!targetChannel) { + this.sendMessage(this.server.name, "403", [this.user.nick, channel, "No such channel"], passedTags); + return false; + } + return targetChannel; + } + + checkIfInChannel(channel: Channel, passedTags: Map = new Map()) { + if (!this.user) return false; + if (!channel.ircUsers.has(this.user.nick)) { + this.sendMessage(this.server.name, "442", [this.user.nick, "You're not on that channel"], passedTags); + return false; + } + return true; + } + + checkMinParams(message: IRCMessage, neededNumber: number) { + if (!this.user) return false; + if (message.params.length < neededNumber) { + this.sendMessage(this.server.name, "461", [this.user.nick, message.command, "Not enough parameters"], message.tags); + return false; + } + return true; + } + routeMessage(data: string) { const message = parseIRCMessage(data); switch (message.command.toUpperCase()) { @@ -61,47 +111,80 @@ export class Client { break; } case 'INVITE': { - const targetUser = this.server.getOrCreateMatrixUser(message.params[0]) - const targetChannel = this.server.ircChannels.get(message.params[1]); + if (!this.checkIfRegistered(message.tags) || !this.checkMinParams(message, 2)) + return; + const targetUser = this.getMatrixUserFromNick(message.params[0], message.tags); + const targetChannel = this.getChannel(message.params[1], message.tags); + if (!this.user || !targetUser || !targetChannel) return; + if (!this.checkIfInChannel(targetChannel, message.tags)) return; + if (targetChannel.matrixUsers.has(targetUser.nick)) { + this.sendMessage(this.server.name, "443", [this.user.nick, targetUser.nick, "is already on channel"], message.tags); + return; + } const reason = (message.params.length === 3) ? message.params[2] : ""; - if (this.user && targetChannel && targetChannel.ircUsers.get(this.user.nick)) - this.user.inviteMatrixUser(this, targetChannel, targetUser, reason, message.tags); + this.user.inviteMatrixUser(this, targetChannel, targetUser, reason, message.tags); break; } case 'KICK': { - const targetChannel = this.server.ircChannels.get(message.params[0]); - const targetMxid = this.server.nickToMxid.get(message.params[1]); - if (!targetMxid) + if (!this.checkIfRegistered(message.tags) || !this.checkMinParams(message, 2)) return; - console.log(targetMxid); + const targetChannel = this.getChannel(message.params[0], message.tags); + const targetUser = this.getMatrixUserFromNick(message.params[1], message.tags); + if (!this.user || !targetUser || !targetChannel) return; + if (!this.checkIfInChannel(targetChannel, message.tags)) return; + if (!targetChannel.matrixUsers.has(targetUser.nick)) { + this.sendMessage(this.server.name, "441", [this.user.nick, targetUser.nick, targetChannel.name, "They aren't on that channel"], message.tags); + return; + } const reason = (message.params.length === 3) ? message.params[2] : ""; - if (this.user && targetChannel && targetChannel.ircUsers.get(this.user.nick)) - this.user.kickMatrixUser(this, targetChannel, targetMxid, reason, message.tags); + this.user.kickMatrixUser(this, targetChannel, targetUser.mxid, reason, message.tags); break; } case 'MODE': { + if (!this.checkIfRegistered(message.tags) || !this.checkMinParams(message, 1) || !this.user) + return; const targetChannel = this.server.ircChannels.get(message.params[0]); - if (this.user && targetChannel && targetChannel.ircUsers.get(this.user.nick)) + if (!targetChannel) { + if (message.params[0] !== this.user.nick) { + this.sendMessage(this.server.name, "502", [this.user.nick, "Can't view mode for other users"], message.tags); + return; + } + this.sendMessage(this.server.name, "221", [this.user.nick, "+i"], message.tags); + return; + } + if (!this.checkIfInChannel(targetChannel, message.tags)) return; + if (message.params.length === 1) targetChannel.sendMode(this, message.tags); + else + targetChannel.handleModeChange(this, message); break; } case 'NAMES': { - const targetChannel = this.server.ircChannels.get(message.params[0]); - if (this.user && targetChannel && targetChannel.ircUsers.get(this.user.nick)) - targetChannel.sendNames(this, message.tags); + if (!this.checkIfRegistered(message.tags) || !this.checkMinParams(message, 1)) + return; + const targetChannel = this.getChannel(message.params[0], message.tags); + if (!this.user || !targetChannel) return; + if (!this.checkIfInChannel(targetChannel, message.tags)) return; + targetChannel.sendNames(this, message.tags); break; } case 'NOTICE': { - if (this.user) { - this.user.sendMessageToMatrix(message, this); - } + if (!this.checkIfRegistered(message.tags) || !this.checkMinParams(message, 2)) + return; + const targetChannel = this.getChannel(message.params[0], message.tags); + if (!this.user || !targetChannel) return; + if (!this.checkIfInChannel(targetChannel, message.tags)) return; + this.user.sendMessageToMatrix(message, this); break; } case 'PART': { - const targetChannel = this.server.ircChannels.get(message.params[0]); + if (!this.checkIfRegistered(message.tags) || !this.checkMinParams(message, 1)) + return; + const targetChannel = this.getChannel(message.params[0], message.tags); + if (!this.user || !targetChannel) return; + if (!this.checkIfInChannel(targetChannel, message.tags)) return; const reason = (message.params.length === 2) ? message.params[1] : ""; - if (this.user && targetChannel && targetChannel.ircUsers.get(this.user.nick)) - this.user.partMatrixRoom(this, targetChannel, reason, message.tags); + this.user.partMatrixRoom(this, targetChannel, reason, message.tags); break; } case 'PING': { @@ -109,21 +192,29 @@ export class Client { break; } case 'PRIVMSG': { - if (this.user) { - this.user.sendMessageToMatrix(message, this); - } + if (!this.checkIfRegistered(message.tags) || !this.checkMinParams(message, 2)) + return; + const targetChannel = this.getChannel(message.params[0], message.tags); + if (!this.user || !targetChannel) return; + if (!this.checkIfInChannel(targetChannel, message.tags)) return; + this.user.sendMessageToMatrix(message, this); break; } case 'TAGMSG': { - if (this.user) { - this.user.sendTagToMatrix(message, this); - } + if (!this.checkIfRegistered(message.tags) || !this.checkMinParams(message, 1)) + return; + const targetChannel = this.getChannel(message.params[0], message.tags); + if (!this.user || !targetChannel) return; + if (!this.checkIfInChannel(targetChannel, message.tags)) return; + this.user.sendTagToMatrix(message, this); break; } case 'TOPIC': { - const targetChannel = this.server.ircChannels.get(message.params[0]); - if (!this.user || !targetChannel || !targetChannel.ircUsers.get(this.user.nick)) - break; + if (!this.checkIfRegistered(message.tags) || !this.checkMinParams(message, 1)) + return; + const targetChannel = this.getChannel(message.params[0], message.tags); + if (!this.user || !targetChannel) return; + if (!this.checkIfInChannel(targetChannel, message.tags)) return; if (message.params.length === 1) { targetChannel.sendTopic(this, message.tags); break; @@ -133,9 +224,12 @@ export class Client { break; } case 'WHO': { - const targetChannel = this.server.ircChannels.get(message.params[0]); - if (this.user && targetChannel && targetChannel.ircUsers.get(this.user.nick)) - targetChannel.sendWho(this, message.tags); + if (!this.checkIfRegistered(message.tags) || !this.checkMinParams(message, 1)) + return; + const targetChannel = this.getChannel(message.params[0], message.tags); + if (!this.user || !targetChannel) return; + if (!this.checkIfInChannel(targetChannel, message.tags)) return; + targetChannel.sendWho(this, message.tags); break; } } diff --git a/src/numerics.ts b/src/numerics.ts index 3ee6030..628a8bc 100644 --- a/src/numerics.ts +++ b/src/numerics.ts @@ -36,7 +36,7 @@ const numerics = { return [nick, "End of MOTD"] }, "433": (nick: string, otherNick: string) => { - return [nick, otherNick, "You can't change nicks on IRC yet"] + return [nick, otherNick, "Nickname is already in use"] }, "900": (mask: string, nick: string) => { return [nick, mask, `You are now logged in as ${nick}`]