Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fallback encoding for non-UTF-8 lines #142

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/clientapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ new Irc.Client({
username: 'ircbot',
gecos: 'ircbot',
encoding: 'utf8',
encoding_fallback: 'cp1252',
version: 'node.js irc-framework',
enable_chghost: false,
enable_echomessage: false,
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"lodash": "^4.17.4",
"middleware-handler": "^0.2.0",
"runes": "^0.4.3",
"socksjs": "^0.5.0"
"socksjs": "^0.5.0",
"utf-8-validate": "^5.0.2"
},
"devDependencies": {
"babel-cli": "^6.26.0",
Expand Down
1 change: 1 addition & 0 deletions src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ module.exports = class IrcClient extends EventEmitter {
username: 'ircbot',
gecos: 'ircbot',
encoding: 'utf8',
encoding_fallback: 'cp1252',
version: 'node.js irc-framework',
enable_chghost: false,
enable_echomessage: false,
Expand Down
12 changes: 12 additions & 0 deletions src/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ module.exports = class Connection extends EventEmitter {
this.setEncoding('utf8');
}

if (options.encoding_fallback) {
this.setEncodingFallback(options.encoding_fallback);
}

// Some transports may emit extra events
transport.on('extra', function(/*event_name, argN*/) {
that.emit.apply(that, arguments);
Expand Down Expand Up @@ -243,6 +247,14 @@ module.exports = class Connection extends EventEmitter {
}
}

setEncodingFallback(encoding) {
this.debugOut('Connection.setEncodingFallback() encoding=' + encoding);

if (this.transport) {
return this.transport.setEncodingFallback(encoding);
}
}


/**
* Process the buffered messages recieved from the IRCd
Expand Down
88 changes: 69 additions & 19 deletions src/transports/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var util = require('util');
var EventEmitter = require('events').EventEmitter;
var Socks = require('socksjs');
var iconv = require('iconv-lite');
var isValidUTF8 = require('utf-8-validate');

var SOCK_DISCONNECTED = 0;
var SOCK_CONNECTING = 1;
Expand All @@ -27,7 +28,8 @@ module.exports = class Connection extends EventEmitter {
this.socket_events = [];

this.encoding = 'utf8';
this.incoming_buffer = '';
this.encoding_fallback = 'cp1252';
this.incoming_buffer = Buffer.from('');
}

isConnected() {
Expand Down Expand Up @@ -77,6 +79,10 @@ module.exports = class Connection extends EventEmitter {
this.setEncoding('utf8');
}

if (options.encoding_fallback) {
this.setEncodingFallback(options.encoding_fallback);
}

this.state = SOCK_CONNECTING;
this.debugOut('Connecting socket..');

Expand Down Expand Up @@ -157,21 +163,16 @@ module.exports = class Connection extends EventEmitter {
}

onSocketData(data) {
this.incoming_buffer += iconv.decode(data, this.encoding);

var lines = this.incoming_buffer.split('\n');
if (lines[lines.length - 1] !== '') {
this.incoming_buffer = lines.pop();
} else {
lines.pop();
this.incoming_buffer = '';
}

lines.forEach(line => this.emit('line', line));
this.incoming_buffer = Buffer.concat(
[this.incoming_buffer, data],
this.incoming_buffer.length + data.length
);

this.splitLines().forEach(
line => this.emit('line', this.decodeBuffer(line))
);
}



disposeSocket() {
this.debugOut('disposeSocket() connected=' + this.isConnected());

Expand All @@ -185,7 +186,6 @@ module.exports = class Connection extends EventEmitter {
}
}


close(force) {
// Cleanly close the socket if we can
if ((this.socket && this.state === SOCK_CONNECTING) || force) {
Expand All @@ -197,19 +197,61 @@ module.exports = class Connection extends EventEmitter {
}
}

// Returns an array of buffer slices containing all currently received
// complete lines, leaving the remainder in the buffer.
splitLines() {
var data = this.incoming_buffer;
var out = [];
var startIndex = 0;
while (true) {
const splitIndex = data.indexOf(0x0a, startIndex) + 1;

if (splitIndex) {
out.push(data.slice(startIndex, splitIndex));
startIndex = splitIndex;
} else {
break;
}
}

if (startIndex < data.length) {
this.incoming_buffer = data.slice(startIndex);
} else {
this.incoming_buffer = Buffer.from('');
}

return out;
}

setEncoding(encoding) {
var encoded_test;

this.debugOut('Connection.setEncoding() encoding=' + encoding);

if (this.testEncoding(encoding)) {
this.encoding = encoding;
return true;
} else {
return false;
}
}

setEncodingFallback(encoding) {
this.debugOut('Connection.setEncodingFallback() encoding=' + encoding);

if (this.testEncoding(encoding)) {
this.encoding_fallback = encoding;
return true;
} else {
return false;
}
}

testEncoding(encoding) {
try {
encoded_test = iconv.encode('TEST', encoding);
const encoded_test = iconv.encode('TEST', encoding);
// This test is done to check if this encoding also supports
// the ASCII charset required by the IRC protocols
// (Avoid the use of base64 or incompatible encodings)
if (encoded_test == 'TEST') { // jshint ignore:line
this.encoding = encoding;
return true;
}
return false;
Expand All @@ -218,6 +260,14 @@ module.exports = class Connection extends EventEmitter {
}
}

decodeBuffer(data) {
if (this.encoding === 'utf8' && this.encoding_fallback && !isValidUTF8(data)) {
return iconv.decode(data, this.encoding_fallback);
} else {
return iconv.decode(data, this.encoding);
}
}

getAddressFamily(addr) {
if (net.isIPv4(addr)) {
return 4;
Expand Down
3 changes: 3 additions & 0 deletions src/transports/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,7 @@ module.exports = class Connection extends EventEmitter {

setEncoding(encoding) {
}

setEncodingFallback(encoding) {
}
};