From 8f655543312da8155814f22594195bf5e2fc8bc3 Mon Sep 17 00:00:00 2001 From: Justin Gallardo Date: Fri, 10 Apr 2015 12:47:20 -0700 Subject: [PATCH 1/2] feature(auth): CAP auth for users --- lib/irc.js | 40 +++++++++++++++++++++++++++---- package.json | 1 + test/data/fixtures.json | 22 +++++++++++++++++ test/test-cap.js | 53 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 test/test-cap.js diff --git a/lib/irc.js b/lib/irc.js index 94a7d8c6..4ab76610 100644 --- a/lib/irc.js +++ b/lib/irc.js @@ -56,12 +56,14 @@ function Client(server, nick, opt) { channelPrefixes: '&#', messageSplit: 512, encoding: false, + capIdentifyMsg: false, webirc: { pass: '', ip: '', user: '' } }; + self.CAP = []; // Features supported by the server // (initial values are RFC 1459 defaults. Zeros signify @@ -557,10 +559,13 @@ function Client(server, nick, opt) { // for sasl case 'CAP': - if (message.args[0] === '*' && - message.args[1] === 'ACK' && - message.args[2] === 'sasl ') // there's a space after sasl - self.send('AUTHENTICATE', 'PLAIN'); + if (message.args[1] === 'NAK') { + self.emit('error', 'CAP ERROR for ": ' + self.CAP.join(' ') + '" CAP sent. Response:' + util.inspect(message)); + } else if (message.args[1] === 'ACK') { + if (message.args[0] === '*' && message.args[2].indexOf('sasl') !== -1) { + self.send('AUTHENTICATE', 'PLAIN'); + } + } break; case 'AUTHENTICATE': if (message.args[0] === '+') self.send('AUTHENTICATE', @@ -718,6 +723,33 @@ Client.prototype.connect = function(retryCount, callback) { self.conn.setEncoding('utf8'); } + self.conn.addListener('connect', function() { + + if (self.opt.capIdentifyMsg) { + self.CAP.push('identify-msg'); + } + + if (self.opt.sasl) { + self.CAP.push('sasl'); + // see http://ircv3.atheme.org/extensions/sasl-3.1 + } + + if (self.CAP.length > 0) { + self.send('CAP REQ', self.CAP.join(' ')); + self.send('CAP', 'END'); + } + + if (self.opt.webirc.ip && self.opt.webirc.pass && self.opt.webirc.host) { + self.send('WEBIRC', self.opt.webirc.pass, self.opt.userName, self.opt.webirc.host, self.opt.webirc.ip); + } else if (self.opt.password) { + self.send('PASS', self.opt.password); + } + self.send('NICK', self.opt.nick); + self.nick = self.opt.nick; + self.send('USER', self.opt.userName, 8, '*', self.opt.realName); + self.emit('connect'); + }); + var buffer = new Buffer(''); self.conn.addListener('data', function(chunk) { diff --git a/package.json b/package.json index 8916a6cd..367951e3 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "Mischa Spiegelmock ", "Justin Gallardo ", "Chris Nehren ", + "Dariusz Niemczyk ", "Henri Niemeläinen " ], "repository": { diff --git a/test/data/fixtures.json b/test/data/fixtures.json index be47f9df..710e526d 100644 --- a/test/data/fixtures.json +++ b/test/data/fixtures.json @@ -148,6 +148,28 @@ "maxLineLength is as expected after 433" ] }, + "CAP": { + "ACK" : { + "sent" : [ + ["CAP REQ identify-msg"], + ["CAP END"] + ], + "received": [ + [":localhost CAP * ACK :identify-msg\r\n"], + [":localhost 001 testbot :Welcome to the Internet Relay Chat Network testbot\r\n", "Received welcome message"] + ] + }, + "NAK": { + "sent": [ + ["CAP REQ doesntExist"], + ["CAP END"] + ], + "received": [ + [":localhost CAP * NAK :doesntExist\r\n"], + [":localhost 001 testbot :Welcome to the Internet Relay Chat Network testbot\r\n", "Received welcome message"] + ] + } + }, "convert-encoding": { "causesException": [ ":ubottu!ubottu@ubuntu/bot/ubottu MODE #ubuntu -bo *!~Brian@* ubottu\r\n", diff --git a/test/test-cap.js b/test/test-cap.js new file mode 100644 index 00000000..cb323807 --- /dev/null +++ b/test/test-cap.js @@ -0,0 +1,53 @@ +var net = require('net'); + +var irc = require('../lib/irc'); +var test = require('tape'); + +var testHelpers = require('./helpers'); + +var expected = testHelpers.getFixtures('CAP'); +var greeting = ':localhost 001 testbot :Welcome to the Internet Relay Chat Network testbot\r\n'; + +test('CAP ACK', function(t) { + runTests(t, 'ACK'); +}); + +test('CAP NAK', function(t) { + runTests(t, 'NAK'); +}); + +function runTests(t, responseType) { + var port = 6667; + var mock = testHelpers.MockIrcd(); + var client = new irc.Client('localhost', 'testbot', { + selfSigned: true, + port: port, + retryCount: 0, + debug: true, + capIdentifyMsg: true + }); + + t.plan(1); + + mock.server.on('connection', function() { + mock.send(expected[responseType].received[0][0]); + mock.send(greeting); + }); + + if (responseType === 'NAK') { + client.on('error', function(err) { + t.equal(mock.outgoing[0], expected[responseType].received[0][0]); + client.disconnect(); + }); + } else { + client.on('registered', function() { + t.equal(mock.outgoing[0], expected[responseType].received[0][0]); + client.disconnect(); + }); + } + + mock.on('end', function() { + mock.close(); + }); + +} From 5cbf599236a87edfceb231fec1e7f4e5ddd95c4e Mon Sep 17 00:00:00 2001 From: Justin Gallardo Date: Wed, 15 Apr 2015 01:14:23 -0700 Subject: [PATCH 2/2] fix(irc): Put CAP logic in the connection handler instead of adding a new listener. --- lib/irc.js | 41 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/lib/irc.js b/lib/irc.js index 4ab76610..062e45a2 100644 --- a/lib/irc.js +++ b/lib/irc.js @@ -639,6 +639,20 @@ Client.prototype.chanData = function(name, create) { }; Client.prototype._connectionHandler = function() { + if (this.opt.capIdentifyMsg) { + this.CAP.push('identify-msg'); + } + + if (this.opt.sasl) { + // see http://ircv3.atheme.org/extensions/sasl-3.1 + this.CAP.push('sasl'); + } + + if (this.CAP.length > 0) { + this.send('CAP REQ', this.CAP.join(' ')); + this.send('CAP', 'END'); + } + if (this.opt.webirc.ip && this.opt.webirc.pass && this.opt.webirc.host) { this.send('WEBIRC', this.opt.webirc.pass, this.opt.userName, this.opt.webirc.host, this.opt.webirc.ip); } @@ -723,33 +737,6 @@ Client.prototype.connect = function(retryCount, callback) { self.conn.setEncoding('utf8'); } - self.conn.addListener('connect', function() { - - if (self.opt.capIdentifyMsg) { - self.CAP.push('identify-msg'); - } - - if (self.opt.sasl) { - self.CAP.push('sasl'); - // see http://ircv3.atheme.org/extensions/sasl-3.1 - } - - if (self.CAP.length > 0) { - self.send('CAP REQ', self.CAP.join(' ')); - self.send('CAP', 'END'); - } - - if (self.opt.webirc.ip && self.opt.webirc.pass && self.opt.webirc.host) { - self.send('WEBIRC', self.opt.webirc.pass, self.opt.userName, self.opt.webirc.host, self.opt.webirc.ip); - } else if (self.opt.password) { - self.send('PASS', self.opt.password); - } - self.send('NICK', self.opt.nick); - self.nick = self.opt.nick; - self.send('USER', self.opt.userName, 8, '*', self.opt.realName); - self.emit('connect'); - }); - var buffer = new Buffer(''); self.conn.addListener('data', function(chunk) {