2014-10-17 02:21:06 +00:00
|
|
|
/* jslint node: true */
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
// ENiGMA½
|
2015-08-20 04:10:18 +00:00
|
|
|
var Log = require('./logger.js').log;
|
|
|
|
var enigmaToAnsi = require('./color_codes.js').enigmaToAnsi;
|
2014-10-17 02:21:06 +00:00
|
|
|
|
2015-08-20 04:10:18 +00:00
|
|
|
var iconv = require('iconv-lite');
|
|
|
|
var assert = require('assert');
|
|
|
|
var _ = require('lodash');
|
2014-10-17 02:21:06 +00:00
|
|
|
|
|
|
|
iconv.extendNodeEncodings();
|
|
|
|
|
|
|
|
exports.ClientTerminal = ClientTerminal;
|
|
|
|
|
|
|
|
function ClientTerminal(output) {
|
|
|
|
this.output = output;
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
var outputEncoding = 'cp437';
|
|
|
|
assert(iconv.encodingExists(outputEncoding));
|
|
|
|
|
|
|
|
// convert line feeds such as \n -> \r\n
|
|
|
|
this.convertLF = true;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Some terminal we handle specially
|
|
|
|
// They can also be found in this.env{}
|
|
|
|
//
|
|
|
|
var termType = 'unknown';
|
|
|
|
var termHeight = 0;
|
|
|
|
var termWidth = 0;
|
2015-07-07 04:37:11 +00:00
|
|
|
var termClient = 'unknown';
|
2014-10-17 02:21:06 +00:00
|
|
|
|
|
|
|
// Raw values set by e.g. telnet NAWS, ENVIRONMENT, etc.
|
|
|
|
this.env = {};
|
|
|
|
|
|
|
|
Object.defineProperty(this, 'outputEncoding', {
|
|
|
|
get : function() {
|
|
|
|
return outputEncoding;
|
|
|
|
},
|
|
|
|
set : function(enc) {
|
|
|
|
if(iconv.encodingExists(enc)) {
|
|
|
|
outputEncoding = enc;
|
|
|
|
} else {
|
2015-04-04 20:41:04 +00:00
|
|
|
Log.warn({ encoding : enc }, 'Unknown encoding');
|
2014-10-17 02:21:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Object.defineProperty(this, 'termType', {
|
|
|
|
get : function() {
|
|
|
|
return termType;
|
|
|
|
},
|
|
|
|
set : function(ttype) {
|
|
|
|
termType = ttype.toLowerCase();
|
|
|
|
|
|
|
|
//
|
|
|
|
// ANSI terminals should be encoded to CP437
|
|
|
|
//
|
2015-06-19 04:17:51 +00:00
|
|
|
// Some terminal types provided by Mercyful Fate / Enthral:
|
|
|
|
// ANSI-BBS
|
|
|
|
// PC-ANSI
|
|
|
|
// QANSI
|
|
|
|
// SCOANSI
|
|
|
|
// VT100
|
|
|
|
// XTERM
|
2015-07-07 04:37:11 +00:00
|
|
|
// * PuTTY
|
2015-06-19 04:17:51 +00:00
|
|
|
// LINUX
|
2015-07-11 22:39:42 +00:00
|
|
|
// * JuiceSSH - also via TERM=
|
2015-06-19 04:17:51 +00:00
|
|
|
// QNX
|
|
|
|
// SCREEN
|
2015-07-07 04:37:11 +00:00
|
|
|
// * ConnectBot
|
2015-06-19 04:17:51 +00:00
|
|
|
//
|
|
|
|
if(this.isANSI()) {
|
2014-10-17 02:21:06 +00:00
|
|
|
this.outputEncoding = 'cp437';
|
|
|
|
} else {
|
|
|
|
// :TODO: See how x84 does this -- only set if local/remote are binary
|
|
|
|
this.outputEncoding = 'utf8';
|
|
|
|
}
|
2015-06-19 04:17:51 +00:00
|
|
|
|
2015-07-07 04:37:11 +00:00
|
|
|
// :TODO: according to this: http://mud-dev.wikidot.com/article:telnet-client-identification
|
|
|
|
// Windows telnet will send "VTNT". If so, set termClient='windows'
|
|
|
|
// there are some others on the page as well
|
|
|
|
|
2015-06-19 04:17:51 +00:00
|
|
|
Log.debug( { encoding : this.outputEncoding }, 'Set output encoding due to terminal type change');
|
2014-10-17 02:21:06 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Object.defineProperty(this, 'termWidth', {
|
|
|
|
get : function() {
|
|
|
|
return termWidth;
|
|
|
|
},
|
|
|
|
set : function(width) {
|
|
|
|
if(width > 0) {
|
|
|
|
termWidth = width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Object.defineProperty(this, 'termHeight', {
|
|
|
|
get : function() {
|
|
|
|
return termHeight;
|
|
|
|
},
|
|
|
|
set : function(height) {
|
|
|
|
if(height > 0) {
|
|
|
|
termHeight = height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2015-07-07 04:37:11 +00:00
|
|
|
|
|
|
|
Object.defineProperty(this, 'termClient', {
|
|
|
|
get : function() {
|
|
|
|
return termClient;
|
|
|
|
},
|
|
|
|
set : function(tc) {
|
|
|
|
termClient = tc;
|
|
|
|
|
|
|
|
Log.debug( { termClient : this.termClient }, 'Set known terminal client');
|
|
|
|
}
|
|
|
|
});
|
2014-10-17 02:21:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ClientTerminal.prototype.isANSI = function() {
|
2015-06-19 04:17:51 +00:00
|
|
|
// :TODO: Others??
|
|
|
|
return [ 'ansi', 'pc-ansi', 'qansi', 'scoansi' ].indexOf(this.termType) > -1;
|
2014-10-17 02:21:06 +00:00
|
|
|
};
|
|
|
|
|
2015-07-11 22:39:42 +00:00
|
|
|
// :TODO: probably need to update these to convert IAC (0xff) -> IACIAC (escape it)
|
|
|
|
|
2015-07-06 01:05:55 +00:00
|
|
|
ClientTerminal.prototype.write = function(s, convertLineFeeds) {
|
|
|
|
this.output.write(this.encode(s, convertLineFeeds));
|
|
|
|
};
|
|
|
|
|
|
|
|
ClientTerminal.prototype.rawWrite = function(s) {
|
|
|
|
this.output.write(s);
|
|
|
|
};
|
|
|
|
|
2015-08-20 04:10:18 +00:00
|
|
|
ClientTerminal.prototype.pipeWrite = function(s) {
|
|
|
|
this.write(enigmaToAnsi(s));
|
|
|
|
};
|
|
|
|
|
2015-07-06 01:05:55 +00:00
|
|
|
ClientTerminal.prototype.encode = function(s, convertLineFeeds) {
|
|
|
|
convertLineFeeds = _.isUndefined(convertLineFeeds) ? this.convertLF : convertLineFeeds;
|
|
|
|
if(convertLineFeeds && _.isString(s)) {
|
|
|
|
s = s.replace(/\n/g, '\r\n');
|
|
|
|
}
|
|
|
|
return iconv.encode(s, this.outputEncoding);
|
|
|
|
};
|
|
|
|
|