From 6888af127bd3f90bbea00f4df4dca2625969265d Mon Sep 17 00:00:00 2001 From: Sheogorath Date: Wed, 17 Jul 2019 00:03:38 +0200 Subject: [PATCH 1/2] Fix: Real world messages IRC is specified to have `\r\n` at the end of each command. If not messages are not considered complete. This patch fixes our implementation to answer with real command aka. things that end with `\r\n`. --- src/server.js | 107 +++++++++++++++++++++------------------- src/user.js | 2 +- test/command_privmsg.js | 4 +- test/command_user.js | 2 +- test/hexchat.js | 23 +++++++++ 5 files changed, 83 insertions(+), 55 deletions(-) create mode 100644 test/hexchat.js diff --git a/src/server.js b/src/server.js index 4bbb541..9437e3b 100644 --- a/src/server.js +++ b/src/server.js @@ -18,62 +18,67 @@ server.create = function create() { let server = net.createServer((socket) => { const user = new User(socket) socket.on('data', function (data) { - let splitted = data.toString("ascii").split("\r\n")[0].split(" ") - let tokenized = [] - let lastParam = -1 - for (let i = 0; i < splitted.length; i++) { - if (lastParam > 0) { - tokenized[lastParam] = tokenized[lastParam] + " " + splitted[i] - } else if (splitted[i].charAt(0) == ":" && i > 0) { - lastParam = i - tokenized[lastParam] = splitted[i].slice(1) - } else { - tokenized[i] = splitted[i] - } - } - let command = tokenized[0].toUpperCase() - switch (command) { - case "PING": - if (tokenized[1] && tokenized[1] === "irc.example.com") { - socket.write("PONG irc.example.com\r\n") - } - break; - case "NICK": - if (tokenized[1]) { - let nickname = tokenized[1] - // nick collision test - if (!Object.keys(userlist).includes(nickname)) { - user.setNickname(nickname) - userlist[user.nickname] = user - } else { - socket.write(ERR_NICKNAMEINUSE, " nickname in use") - } + data.toString("ascii").split("\r\n").forEach(function (commandline) { + if (commandline == "") { + return + } + let splitted = commandline.split(" ") + let tokenized = [] + let lastParam = -1 + for (let i = 0; i < splitted.length; i++) { + if (lastParam > 0) { + tokenized[lastParam] = tokenized[lastParam] + " " + splitted[i] + } else if (splitted[i].charAt(0) == ":" && i > 0) { + lastParam = i + tokenized[lastParam] = splitted[i].slice(1) + } else { + tokenized[i] = splitted[i] + } + } + let command = tokenized[0].toUpperCase() + switch (command) { + case "PING": + if (tokenized[1] && tokenized[1] === "irc.example.com") { + socket.write("PONG irc.example.com\r\n") + } + break; + case "NICK": + if (tokenized[1]) { + let nickname = tokenized[1] + // nick collision test + if (!Object.keys(userlist).includes(nickname)) { + user.setNickname(nickname) + userlist[user.nickname] = user + } else { + socket.write(ERR_NICKNAMEINUSE, " nickname in use") + } - } else { - socket.write(ERR_NONICKNAMEGIVEN, "ERROR: NO NICKNAME PROVIDED") - } + } else { + socket.write(ERR_NONICKNAMEGIVEN, "ERROR: NO NICKNAME PROVIDED") + } - break; + break; - case "USER": - // ignoring servername and hostname, not useful these days - user.register(tokenized[1], tokenized[4]) - let address = user.getAddress() - socket.write(`001 ${user.nickname} :Welcome to the example IRC Project ${user.nickname}!~${user.username}@${address}`) - break; - case "PRIVMSG": - let target = userlist[tokenized[1]] - let message = tokenized[2] + case "USER": + // ignoring servername and hostname, not useful these days + user.register(tokenized[1], tokenized[4]) + let address = user.getAddress() + socket.write(`001 ${user.nickname} :Welcome to the example IRC Project ${user.nickname}!~${user.username}@${address}\r\n`, "ascii") + break; + case "PRIVMSG": + let target = userlist[tokenized[1]] + let message = tokenized[2] - target.sendMsg(user, message) - break; - case "QUIT": - server.closeConnection(user.nickname) - break; - default: - console.error(`Unknown command: ${command}`); - } + target.sendMsg(user, message) + break; + case "QUIT": + server.closeConnection(user.nickname) + break; + default: + console.error(`Unknown command: ${command}`); + } + }); }).on('error', (err) => { console.error(err); }) diff --git a/src/user.js b/src/user.js index 2eb23e0..fdb4678 100644 --- a/src/user.js +++ b/src/user.js @@ -35,7 +35,7 @@ function User(socket) { this.sendMsg = function (from, message) { - this.connection.write(`:${from.nickname} PRIVMSG ${this.nickname} :${message}`) + this.connection.write(`:${from.nickname} PRIVMSG ${this.nickname} :${message}\r\n`, "ascii") } } diff --git a/test/command_privmsg.js b/test/command_privmsg.js index 75b5aa6..d464e0b 100644 --- a/test/command_privmsg.js +++ b/test/command_privmsg.js @@ -12,7 +12,7 @@ describe("PRIVMSG OK", function () { mockedSock.write = function (data) { let answer = data.toString("ascii") if (answer.indexOf("PRIVMSG") >= 0) { - assert.equal(answer, ":some_nick PRIVMSG some_nick :I'm a message") + assert.equal(answer, ":some_nick PRIVMSG some_nick :I'm a message\r\n") done() } } @@ -45,7 +45,7 @@ describe("PRIVMSG OK", function () { mockedSock2.write = function (data) { let answer = data.toString("ascii") if (answer.indexOf("PRIVMSG") >= 0) { - assert.equal(answer, ":some_nick PRIVMSG other_nick :I'm a message") + assert.equal(answer, ":some_nick PRIVMSG other_nick :I'm a message\r\n") done() } } diff --git a/test/command_user.js b/test/command_user.js index 5d459c8..23c30f0 100644 --- a/test/command_user.js +++ b/test/command_user.js @@ -10,7 +10,7 @@ describe("USER OK", function () { return { port: 12346, family: 'IPv4', address: '127.0.0.1' } } mockedSock.write = function (data) { - assert.equal(data.toString("ascii"), "001 some_nick :Welcome to the example IRC Project some_nick!~guest@127.0.0.1") + assert.equal(data.toString("ascii"), "001 some_nick :Welcome to the example IRC Project some_nick!~guest@127.0.0.1\r\n") done() } mockedSock.destroy = function () { diff --git a/test/hexchat.js b/test/hexchat.js new file mode 100644 index 0000000..a491a69 --- /dev/null +++ b/test/hexchat.js @@ -0,0 +1,23 @@ +const assert = require('assert'); +const EventEmitter = require('events'); +const IRCServer = require("../src/server.js"); + +describe("hexchat", function () { + it("should handle hexchat inital command list", function (done) { + const server = IRCServer.create() + let mockedSock = new EventEmitter() + mockedSock.address = function() { + return { port: 12346, family: 'IPv4', address: '127.0.0.1' } + } + mockedSock.write = function (data) { + assert.equal(data.toString("ascii"), "001 some_nick :Welcome to the example IRC Project some_nick!~guest@127.0.0.1\r\n") + done() + } + mockedSock.destroy = function () { + done("Destroyed socket without answering") + } + + server.emit("connection", mockedSock) + mockedSock.emit('data', Buffer.from("CAP LS 302\r\nNICK some_nick\r\nUSER guest tolmoon tolsun :Ronnie Reagan\r\n", "ascii")) + }) +}) From 7cff627c6e62bfe1f1ca68a8bcfed9652e4b337a Mon Sep 17 00:00:00 2001 From: Sheogorath Date: Wed, 17 Jul 2019 00:08:24 +0200 Subject: [PATCH 2/2] Fix: Add server prefix to indicate server notices --- src/server.js | 2 +- test/command_user.js | 2 +- test/hexchat.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server.js b/src/server.js index 9437e3b..c328a8a 100644 --- a/src/server.js +++ b/src/server.js @@ -64,7 +64,7 @@ server.create = function create() { // ignoring servername and hostname, not useful these days user.register(tokenized[1], tokenized[4]) let address = user.getAddress() - socket.write(`001 ${user.nickname} :Welcome to the example IRC Project ${user.nickname}!~${user.username}@${address}\r\n`, "ascii") + socket.write(`:irc.example.com 001 ${user.nickname} :Welcome to the example IRC Project ${user.nickname}!~${user.username}@${address}\r\n`, "ascii") break; case "PRIVMSG": let target = userlist[tokenized[1]] diff --git a/test/command_user.js b/test/command_user.js index 23c30f0..d937f1b 100644 --- a/test/command_user.js +++ b/test/command_user.js @@ -10,7 +10,7 @@ describe("USER OK", function () { return { port: 12346, family: 'IPv4', address: '127.0.0.1' } } mockedSock.write = function (data) { - assert.equal(data.toString("ascii"), "001 some_nick :Welcome to the example IRC Project some_nick!~guest@127.0.0.1\r\n") + assert.equal(data.toString("ascii"), ":irc.example.com 001 some_nick :Welcome to the example IRC Project some_nick!~guest@127.0.0.1\r\n") done() } mockedSock.destroy = function () { diff --git a/test/hexchat.js b/test/hexchat.js index a491a69..b90f61f 100644 --- a/test/hexchat.js +++ b/test/hexchat.js @@ -10,7 +10,7 @@ describe("hexchat", function () { return { port: 12346, family: 'IPv4', address: '127.0.0.1' } } mockedSock.write = function (data) { - assert.equal(data.toString("ascii"), "001 some_nick :Welcome to the example IRC Project some_nick!~guest@127.0.0.1\r\n") + assert.equal(data.toString("ascii"), ":irc.example.com 001 some_nick :Welcome to the example IRC Project some_nick!~guest@127.0.0.1\r\n") done() } mockedSock.destroy = function () {