Browse Source

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`.
feature/index.js
Sheogorath 5 years ago
parent
commit
6888af127b
No known key found for this signature in database GPG Key ID: 1F05CC3635CDDFFD
  1. 107
      src/server.js
  2. 2
      src/user.js
  3. 4
      test/command_privmsg.js
  4. 2
      test/command_user.js
  5. 23
      test/hexchat.js

107
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);
})

2
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")
}
}

4
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()
}
}

2
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 () {

23
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"))
})
})
Loading…
Cancel
Save