From 6db8e096bc80ba8a948fc68bc8d68ca7819e845c Mon Sep 17 00:00:00 2001 From: Andris Reinman Date: Mon, 18 Sep 2017 17:10:35 +0300 Subject: [PATCH] Updated nick handling --- config/irc.toml | 5 +- indexes.yaml | 11 +++ lib/attachments/gridstore-storage.js | 6 +- lib/irc/connection.js | 142 ++++++++++++++++++++++++++- package.json | 8 +- 5 files changed, 159 insertions(+), 13 deletions(-) diff --git a/config/irc.toml b/config/irc.toml index e0d9367b..41b9084e 100644 --- a/config/irc.toml +++ b/config/irc.toml @@ -10,7 +10,10 @@ secure=false [motd] source="message" # "message" or "file" #file="/path/to/motd" -message="Set MOTD message with the server config option \u0002irc.motd\u0002" +message=""" +Set MOTD message with the server config option \u0002irc.motd\u0002. +Once you have set or changed the message then reload the server by +running \u0002systemctl reload wildduck\u0002 or send a SIGHUP message to it""" [tls] # If certificate path is not defined, use global or built-in self-signed certs diff --git a/indexes.yaml b/indexes.yaml index 96c13fa2..0f61b63e 100644 --- a/indexes.yaml +++ b/indexes.yaml @@ -335,3 +335,14 @@ indexes: expireAfterSeconds: 15552000 key: updated: 1 + +# Indexes for IRC + +- collection: nicks + index: + name: irc_nicks + unique: true + key: + ns: 1 + unickview: 1 + user: 1 diff --git a/lib/attachments/gridstore-storage.js b/lib/attachments/gridstore-storage.js index 295cadde..c641bfcd 100644 --- a/lib/attachments/gridstore-storage.js +++ b/lib/attachments/gridstore-storage.js @@ -51,7 +51,7 @@ class GridstoreStorage { transferEncoding: attachment.transferEncoding }; - if (!isNaN(metadata.m)) { + if (isNaN(metadata.m) || typeof metadata.m !== 'number') { errors.notify(new Error('Invalid magic "' + metadata.m + '" for ' + id)); } @@ -198,7 +198,7 @@ class GridstoreStorage { } delete(id, magic, callback) { - if (!isNaN(magic)) { + if (isNaN(magic) || typeof magic !== 'number') { errors.notify(new Error('Invalid magic "' + magic + '" for ' + id)); } this.gridfs.collection(this.bucketName + '.files').findOneAndUpdate({ @@ -239,7 +239,7 @@ class GridstoreStorage { } update(ids, count, magic, callback) { - if (!isNaN(magic)) { + if (isNaN(magic) || typeof magic !== 'number') { errors.notify(new Error('Invalid magic "' + magic + '" for ' + ids)); } // update attachments diff --git a/lib/irc/connection.js b/lib/irc/connection.js index 2b0b5144..5233dc23 100644 --- a/lib/irc/connection.js +++ b/lib/irc/connection.js @@ -5,6 +5,7 @@ const crypto = require('crypto'); const EventEmitter = require('events'); const os = require('os'); const codes = require('./codes'); +const db = require('../db'); const PING_TIMEOUT = 120 * 1000; const SOCKET_TIMEOUT = 5 * 60 * 1000; @@ -412,16 +413,128 @@ class IRCConnection extends EventEmitter { return next(); } + //this.session.clientHostname = 'example.com'; this.session.user = result.username; - next(null, { - id: result.user, - username: result.username + db.users.collection('users').findOne({ _id: result.user }, { + fields: { + _id: true, + username: true, + address: true, + name: true, + ns: true + } + }, (err, userData) => { + if (err) { + return next(err); + } + + let ns = userData.ns; + + if (userData.address) { + let parts = userData.address.split('@'); + this.session.user = parts.shift(); + this.session.clientHostname = parts.join('@'); + if (!ns) { + ns = this.session.clientHostname; + } + } + + this.session.ns = ns || 'root'; + + next(null, { + id: userData._id, + username: userData.username + }); }); } ); } + getNick(nick, next) { + let ns = this.session.ns; + let unickview = nick.toLowerCase().replace(/\./g, ''); + let user = this.session.auth.id; + + let verifyUser = done => { + if (unickview === this.session.auth.username.replace(/\./g, '')) { + return done(); + } + db.users.collection('users').findOne({ unameview: unickview }, { + fields: { + _id: true, + username: true + } + }, (err, userData) => { + if (err) { + return next(err); + } + if (userData && userData._id.toString() !== this.session.auth.id.toString()) { + return next(new Error('Can not acquire reserved nick')); + } + return done(); + }); + }; + + verifyUser(() => { + db.database.collection('nicks').insertOne({ + ns, + unickview, + nick, + user + }, (err, r) => { + if (err) { + if (err.code === 11000) { + return db.database.collection('nicks').findOne({ + ns, + unickview + }, (err, nickData) => { + if (err) { + return next(err); + } + if (!nickData) { + return next(new Error('Race condition in acquireing nick')); + } + if (nickData.user.toString() === user.toString()) { + return next(null, nickData._id); + } + return next(new Error('Requested nick is already in use')); + }); + } + return next(err); + } + let insertId = r && r.insertedId; + if (!insertId) { + return next(new Error('Failed to set up nick')); + } + + // try to remove old nicks + db.database.collection('nicks').deleteOne({ + ns, + unickview: { $ne: unickview }, + user + }, () => next(null, insertId)); + }); + }); + } + + verifyNickChange(currentSource, next) { + this.getNick(this.session.nick, err => { + if (err) { + currentSource = currentSource || this.getFormattedName(); + this.send({ verb: 'ERR_UNAVAILRESOURCE', params: err.message }); + this.session.nick = this.session.user; + } + + if (currentSource) { + this.send({ source: currentSource, verb: 'NICK', target: false, params: this.session.nick }); + } + + this.server.nick(this); + return next(); + }); + } + checkAuth() { if (!this.session.auth) { this.send({ verb: 'ERR_NOTREGISTERED', params: 'PRIVMSG', message: 'Authentication required to chat in this server' }); @@ -475,18 +588,28 @@ class IRCConnection extends EventEmitter { } command_NICK(params, next) { + let currentSource = this.getFormattedName(); + if (params.length > 1) { this.send({ verb: 'ERR_ERRONEUSNICKNAME', params, message: 'Erroneous Nickname' }); + return next(); } else if (!params.length) { this.send({ verb: 'ERR_NEEDMOREPARAMS', params, message: 'Not enough parameters' }); + return next(); } else if (this.server.disabledNicks.includes(params[0].trim().toLowerCase())) { this.send({ verb: 'ERR_ERRONEUSNICKNAME', params, message: 'Erroneous Nickname' }); + return next(); } else { this.session.nick = params[0]; } + this.checkSessionStart(); - this.server.nick(this); - return next(); + + if (this.session.auth) { + this.verifyNickChange(currentSource, next); + } else { + next(); + } } command_PASS(params, next) { @@ -526,6 +649,7 @@ class IRCConnection extends EventEmitter { if (auth) { this.session.auth = auth; this.checkSessionStart(); + return this.verifyNickChange(false, next); } return next(); }); @@ -731,6 +855,7 @@ class IRCConnection extends EventEmitter { target: this.session.nick, message: 'You are now identified for ' + this.session.user }); + return this.verifyNickChange(false, next); } else { this.send({ source: ':NickServ!NickServ@services.', @@ -806,6 +931,7 @@ class IRCConnection extends EventEmitter { message: 'You are now logged in as ' + this.session.user }); this.send({ verb: 'RPL_SASLSUCCESS', target: this.session.nick || '*', message: 'SASL authentication successful' }); + return this.verifyNickChange(false, next); } else { this.send({ verb: 'ERR_SASLFAIL', target: this.session.nick || '*', message: 'SASL authentication failed' }); } @@ -816,6 +942,12 @@ class IRCConnection extends EventEmitter { return next(); } + + command_NN(params, next) { + this.session.nick = params[0]; + this.server.nick(this); + next(); + } } module.exports = IRCConnection; diff --git a/package.json b/package.json index 6b87f069..8c0e273c 100644 --- a/package.json +++ b/package.json @@ -25,14 +25,14 @@ "dependencies": { "addressparser": "1.0.1", "bcryptjs": "2.4.3", - "bugsnag": "1.12.1", + "bugsnag": "1.12.2", "generate-password": "1.3.0", "he": "1.1.1", "html-to-text": "3.3.0", "humanname": "0.2.2", "humanparser": "1.5.0", "iconv-lite": "0.4.19", - "joi": "10.6.0", + "joi": "11.0.1", "js-yaml": "3.10.0", "libbase64": "0.2.0", "libmime": "3.1.0", @@ -49,14 +49,14 @@ "qrcode": "0.9.0", "redfour": "1.0.2", "redis": "2.8.0", - "restify": "5.2.0", + "restify": "6.0.0", "seq-index": "1.1.0", "smtp-server": "3.1.0", "speakeasy": "2.0.0", "tlds": "1.196.0", "utf7": "1.0.2", "uuid": "3.1.0", - "wild-config": "1.3.2" + "wild-config": "1.3.5" }, "repository": { "type": "git",