15 Commits

Author SHA1 Message Date
wayne f816cd1ada added test case to not send to unregistered users 5 years ago
Jenkins d189abba50 Merge commit '6ffedcd74e47cd0c71119200cb5078a41b2e2ef5' into HEAD 5 years ago
Sheogorath 6ffedcd74e
Add test for wrong password 5 years ago
Sheogorath 0768520839
Fix password comand behavior to match real protocol 5 years ago
Jenkins c8afd9959a Merge commit 'e9cba7c2a4566139ca4d32a97e8043b87fb1e2fd' into HEAD 5 years ago
Sheogorath e9cba7c2a4
Add README.md 5 years ago
Jenkins 589d8ce18a Merge commit '4ef2a6f885ac124e4bd8d086f338eb6079fcb8dc' into HEAD 5 years ago
Sheogorath 4ef2a6f885
Merge branch 'develop' of https://gogs.informatik.hs-fulda.de/sheogorath/ircd-js into feature/index.js 5 years ago
Sheogorath 7cff627c6e
Fix: Add server prefix to indicate server notices 5 years ago
Sheogorath 6888af127b
Fix: Real world messages 5 years ago
Jenkins 933454d2d9 Merge commit '806dee970a4df81cae83ee69c97ce7aba4fd3df7' into HEAD 5 years ago
Sheogorath 806dee970a
Feature: Add index.js that starts the actual server 5 years ago
Jenkins daf1a1b804 Merge commit '3b00c69e073100ceccec3062446627fb2e8810fc' into HEAD 5 years ago
Wayne 3b00c69e07 added command_pass test + implementation 5 years ago
Jenkins c5b505a819 Merge commit '8a00bf6cb860bec9ecf0e8318615b2daa71a9db7' into HEAD 5 years ago
  1. 18
      README.md
  2. 3
      index.js
  3. 126
      src/server.js
  4. 11
      src/user.js
  5. 57
      test/command_pass.js
  6. 59
      test/command_privmsg.js
  7. 2
      test/command_user.js
  8. 23
      test/hexchat.js

18
README.md

@ -0,0 +1,18 @@
ircd-js
===
A simple IRCd project to provide an IRCd.
Running
===
```bash
# Install all dev dependencies
npm install
# Run mocha tests
npm test
# Start the index.js
npm start
```

3
index.js

@ -0,0 +1,3 @@
const server = require("./src/server.js")
server.create().listen(6667)

126
src/server.js

@ -11,69 +11,95 @@ const ERR_NONICKNAMEGIVEN = '431'
const ERR_NICKNAMEINUSE = '433'
const ERR_NEEDMOREPARAMS = '461'
let server = {}
server.create = function create() {
server.create = function create(config = {}) {
let password
if (config.password) {
password = config.password
}
let userlist = {}
let server = net.createServer((socket) => {
const user = new User(socket)
if (!config.password) {
user.authenticated = true
}
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]
data.toString("ascii").split("\r\n").forEach(function (commandline) {
if (commandline == "") {
return
}
}
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")
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]
}
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
}
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_NICKNAMEINUSE, " nickname in use")
socket.write(ERR_NONICKNAMEGIVEN, "ERROR: NO NICKNAME PROVIDED")
}
break;
} else {
socket.write(ERR_NONICKNAMEGIVEN, "ERROR: NO NICKNAME PROVIDED")
}
case "USER":
// ignoring servername and hostname, not useful these days
user.register(tokenized[1], tokenized[4])
let address = user.getAddress()
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]]
let message = tokenized[2]
if (target.registered === false) {
socket.write(`:irc.example.com ${ERR_NOSUCHNICK} ${target} no such nick/channel`)
} else {
target.sendMsg(user, message)
}
break;
case "QUIT":
server.closeConnection(user.nickname)
break;
case "PASS":
if (tokenized[1] === config.password) {
user.authenticated = true
} else {
user.closeConnection()
}
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]
target.sendMsg(user, message)
break;
case "QUIT":
server.closeConnection(user.nickname)
break;
default:
console.error(`Unknown command: ${command}`);
}
default:
console.error(`Unknown command: ${command}`);
}
});
}).on('error', (err) => {
console.error(err);
})

11
src/user.js

@ -1,5 +1,6 @@
function User(socket) {
this.registered = false
this.authenticated = false
this.nickname = ""
this.connection = socket
this.realname = ""
@ -12,8 +13,14 @@ function User(socket) {
this.getNickname = function(nickname) {
return this.nickname
}
this.setPassword = function (password) {
this.password = password
}
this.register = function(username, realname) {
if (!this.authenticated) {
this.connection.destroy()
}
this.username = username
this.realname = realname
this.registered = true
@ -30,12 +37,12 @@ function User(socket) {
}
this.closeConnection = function () {
socket.destroy()
this.connection.destroy()
}
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")
}
}

57
test/command_pass.js

@ -0,0 +1,57 @@
const assert = require('assert');
const EventEmitter = require('events');
const IRCServer = require("../src/server.js");
describe("PASS OK", function () {
it("should handle a PASS command -> PASS some_passwd", function (done) {
const server = IRCServer.create({
password: "itsJustATest"
})
let mockedSock = new EventEmitter()
mockedSock.address = function() {
return { port: 12346, family: 'IPv4', address: '127.0.0.1' }
}
mockedSock.write = function (data) {
return
}
mockedSock.destroy = function () {
done("Destroyed socket without answering")
}
server.emit("connection", mockedSock)
mockedSock.emit('data', Buffer.from("PASS itsJustATest\r\n", "ascii"))
mockedSock.emit('data', Buffer.from("NICK some_nick\r\n", "ascii"))
mockedSock.emit('data', Buffer.from("USER guest tolmoon tolsun :Ronnie Reagan\r\n", "ascii"))
user = server.getUserlist()["some_nick"]
assert.equal(user["registered"], true)
done()
})
it("should end connection on wrong password", function (done) {
const server = IRCServer.create({
password: "itsJustATest"
})
let mockedSock = new EventEmitter()
mockedSock.address = function() {
return { port: 12346, family: 'IPv4', address: '127.0.0.1' }
}
mockedSock.write = function (data) {
return
}
mockedSock.destroy = function () {
// connection has to be destroyed
done("")
}
server.emit("connection", mockedSock)
mockedSock.emit('data', Buffer.from("PASS wrongpassword\r\n", "ascii"))
})
})

59
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()
}
}
@ -29,8 +29,8 @@ describe("PRIVMSG OK", function () {
it("should handle a PRIVMSG command for multiple users -> PRIVMSG other_nick :I'm a message", function (done) {
const server = IRCServer.create()
let mockedSock1 = new EventEmitter()
mockedSock1.address = function() {
return { port: 12346, family: 'IPv4', address: '127.0.0.1' }
mockedSock1.address = function () {
return {port: 12346, family: 'IPv4', address: '127.0.0.1'}
}
mockedSock1.write = function (data) {
return
@ -39,15 +39,15 @@ describe("PRIVMSG OK", function () {
done("Destroyed socket without answering")
}
let mockedSock2 = new EventEmitter()
mockedSock2.address = function() {
return { port: 12346, family: 'IPv4', address: '127.0.0.1' }
mockedSock2.address = function () {
return {port: 12346, family: 'IPv4', address: '127.0.0.1'}
}
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")
done()
}
let answer = data.toString("ascii")
if (answer.indexOf("PRIVMSG") >= 0) {
assert.equal(answer, ":some_nick PRIVMSG other_nick :I'm a message\r\n")
done()
}
}
mockedSock2.destroy = function () {
done("Destroyed socket without answering")
@ -60,6 +60,45 @@ describe("PRIVMSG OK", function () {
mockedSock2.emit('data', Buffer.from("NICK other_nick\r\n", "ascii"))
mockedSock2.emit('data', Buffer.from("USER guest tolmoon tolsun :Ronnie Reagan\r\n", "ascii"))
mockedSock1.emit('data', Buffer.from("PRIVMSG other_nick :I'm a message\r\n", "ascii"))
})
it("dont send message to unregistered user", function (done) {
const server = IRCServer.create()
let mockedSock1 = new EventEmitter()
mockedSock1.address = function () {
return {port: 12346, family: 'IPv4', address: '127.0.0.1'}
}
mockedSock1.write = function (data) {
answer = data.toString('ascii')
if (answer.indexOf("401")>=0) {
assert.equal(answer, ":irc.example.com 401 some_nick no such nick/channel")
}
done()
}
mockedSock1.destroy = function () {
done("Destroyed socket without answering")
}
let mockedSock2 = new EventEmitter()
mockedSock2.address = function () {
return {port: 12346, family: 'IPv4', address: '127.0.0.1'}
}
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\r\n")
done("I got a message that I shouldn't have seen")
}
}
mockedSock2.destroy = function () {
done("Destroyed socket without answering")
}
server.emit("connection", mockedSock1)
server.emit("connection", mockedSock2)
mockedSock1.emit('data', Buffer.from("NICK some_nick\r\n", "ascii"))
mockedSock1.emit('data', Buffer.from("USER guest tolmoon tolsun :Ronnie Reagan\r\n", "ascii"))
mockedSock2.emit('data', Buffer.from("NICK other_nick\r\n", "ascii"))
mockedSock1.emit('data', Buffer.from("PRIVMSG other_nick :I'm a message\r\n", "ascii"))
})
})

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"), ":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 () {

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"), ":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 () {
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