Compare commits
10 commits
2e9d5071d9
...
29f17aa874
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29f17aa874 | ||
|
|
c38fb5d107 | ||
|
|
9004afebdc | ||
|
|
f2256500e1 | ||
|
|
4940bf86ae | ||
|
|
4059bf2983 | ||
|
|
c4d10ba33d | ||
|
|
2f40fc5387 | ||
|
|
c824ea924c | ||
|
|
5b790718d8 |
8 changed files with 247 additions and 163 deletions
2
Makefile
2
Makefile
|
|
@ -23,7 +23,7 @@ install: all
|
|||
mkdir -p ${CHROOT}/var/www
|
||||
cp ${WEBASSETS} ${CHROOT}/var/www
|
||||
cp rlgwebd.service /usr/lib/systemd/system
|
||||
if test ! -f /etc/rlgwebd.conf; cp rlgwebd.conf /etc; fi
|
||||
if test ! -f /etc/rlgwebd.conf; then cp rlgwebd.conf /etc; fi
|
||||
|
||||
# Libraries are not removed. Something else might be using them.
|
||||
uninstall:
|
||||
|
|
|
|||
21
README.txt
21
README.txt
|
|
@ -5,12 +5,11 @@ browser. It is intended to be compatible with dgamelaunch.
|
|||
|
||||
Node
|
||||
---
|
||||
RLGWebD currently works with Node v0.10.
|
||||
RLGWebD is currently being updated to work with Node v10.x.
|
||||
|
||||
It requires the 'posix', 'pty.js', and 'websocket' modules. Currently,
|
||||
it expects them to be installed in the global location, which is
|
||||
"/usr/lib/node_modules". It is planned to eventually use a different
|
||||
location so that npm will not need to run as root.
|
||||
It requires the 'posix', 'node-pty', and 'websocket' modules. Currently,
|
||||
it expects them to be installed in "/var/local/lib/node_modules". It
|
||||
is not recommended to run npm as root when installing the modules.
|
||||
|
||||
init
|
||||
---
|
||||
|
|
@ -22,14 +21,11 @@ a proper initscript, but it could form the basis of one.
|
|||
|
||||
Configuration
|
||||
---
|
||||
You can set some options by changing some variables in the first few
|
||||
lines of the rlgwebd script:
|
||||
A configuration file is installed at /etc/rlgwebd.conf. It contains a
|
||||
list of options.
|
||||
|
||||
Option Variable Default
|
||||
|
||||
Chroot path chrootDir /var/dgl
|
||||
Username dropToUser rodney
|
||||
Server port httpPort 8080
|
||||
If the domain_name option and the SSL-related options are set, rlgwebd
|
||||
will use HTTPS instead of insecure HTTP.
|
||||
|
||||
If you change the chroot location, change it in the first line of the
|
||||
Makefile too.
|
||||
|
|
@ -59,6 +55,7 @@ Running "make install" will:
|
|||
Copy the C programs and the libraries they need into the chroot
|
||||
Install the main RLGWebD script in /usr/local/bin
|
||||
Place the systemd unit file in the proper directory
|
||||
Copy a configuration file into /etc
|
||||
|
||||
If you don't use systemd, or want to change the installation locations,
|
||||
you will have to edit the Makefile.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
NODE_PATH=/usr/lib/node_modules
|
||||
NODE_PATH=/var/local/lib/node_modules
|
||||
LOGFILE=/var/log/rlgwebd.log
|
||||
CTLSOCKET=/var/run/rlgwebd.sock
|
||||
CTLSOCKET=/var/run/rlgwebd/rlgwebd.sock
|
||||
RLGWEBDJS=/usr/local/bin/rlgwebd
|
||||
|
||||
export NODE_PATH
|
||||
|
|
|
|||
154
rlgwebd
154
rlgwebd
|
|
@ -1,35 +1,38 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
var http = require('http');
|
||||
var https = require('https');
|
||||
var net = require('net');
|
||||
var url = require('url');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var events = require('events');
|
||||
var child_process = require('child_process');
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
const net = require('net');
|
||||
const url = require('url');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const events = require('events');
|
||||
const child_process = require('child_process');
|
||||
// Dependencies
|
||||
var posix = require("posix");
|
||||
var pty = require("pty.js");
|
||||
var WebSocketServer = require("websocket").server;
|
||||
const posix = require("posix");
|
||||
const pty = require("node-pty");
|
||||
const WebSocketServer = require("websocket").server;
|
||||
|
||||
const errorcodes = [ "Generic Error", "Not logged in", "Invalid data",
|
||||
"Login failed", "Already playing", "Game launch failed",
|
||||
"Server shutting down", "Game not in progress" ];
|
||||
|
||||
/* Default options */
|
||||
var rlgwebd_options = {
|
||||
control_socket: "/var/run/rlgwebd.sock",
|
||||
http_port: 8080,
|
||||
https_port: 8081,
|
||||
control_socket: "/var/run/rlgwebd/rlgwebd.sock",
|
||||
port: 8080,
|
||||
chrootDir: "/var/dgl/",
|
||||
username: "rodney",
|
||||
static_root: "/var/www/"
|
||||
};
|
||||
|
||||
/* Read configuration from a file */
|
||||
var config_file = "/etc/rlgwebd.conf";
|
||||
const config_file = "/etc/rlgwebd.conf";
|
||||
var config_lines = read_or_die(config_file, "Configuration file").toString().split('\n');
|
||||
|
||||
for (var i = 0; i < config_lines.length; i++) {
|
||||
if (config_lines[i].length > 0 && config_lines[i][0] != '#') {
|
||||
var config_fields = config_lines[i].split('=');
|
||||
for (let conf_line of config_lines) {
|
||||
if (conf_line.length > 0 && conf_line[0] != '#') {
|
||||
var config_fields = conf_line.split('=');
|
||||
if (config_fields.length < 2)
|
||||
continue;
|
||||
var option_name = config_fields[0].trim();
|
||||
|
|
@ -44,13 +47,13 @@ if ("domain_name" in rlgwebd_options && "keyfile" in rlgwebd_options &&
|
|||
"certfile" in rlgwebd_options)
|
||||
rlgwebd_options["use_https"] = true;
|
||||
|
||||
var clearbufs = [
|
||||
new Buffer([27, 91, 72, 27, 91, 50, 74]), // xterm: CSI H CSI 2J
|
||||
new Buffer([27, 91, 72, 27, 91, 74]) // screen: CSI H CSI J
|
||||
const clearbufs = [
|
||||
Buffer.from([27, 91, 72, 27, 91, 50, 74]), // xterm: CSI H CSI 2J
|
||||
Buffer.from([27, 91, 72, 27, 91, 74]) // screen: CSI H CSI J
|
||||
];
|
||||
|
||||
/* Data on the games available. */
|
||||
var games = {
|
||||
const games = {
|
||||
"rogue3": {
|
||||
"name": "Rogue V3",
|
||||
"uname": "rogue3",
|
||||
|
|
@ -110,7 +113,7 @@ function BaseGame() {
|
|||
this.watchers = [];
|
||||
/* replaybuf holds the output since the last screen clear, so watchers can
|
||||
* begin with a complete screen. replaylen is the number of bytes stored. */
|
||||
this.replaybuf = new Buffer(1024);
|
||||
this.replaybuf = Buffer.alloc(1024);
|
||||
this.replaylen = 0;
|
||||
/* Time of last activity. */
|
||||
this.lasttime = new Date();
|
||||
|
|
@ -126,12 +129,12 @@ BaseGame.prototype.tag = function () {
|
|||
BaseGame.prototype.framepush = function(chunk) {
|
||||
/* If this chunk resets the screen, discard what preceded it. */
|
||||
if (isclear(chunk)) {
|
||||
this.replaybuf = new Buffer(1024);
|
||||
this.replaybuf = Buffer.alloc(1024);
|
||||
this.replaylen = 0;
|
||||
}
|
||||
/* Make sure there's space. */
|
||||
while (this.replaybuf.length < chunk.length + this.replaylen) {
|
||||
var nbuf = new Buffer(this.replaybuf.length * 2);
|
||||
var nbuf = Buffer.alloc(this.replaybuf.length * 2);
|
||||
this.replaybuf.copy(nbuf, 0, 0, this.replaylen);
|
||||
this.replaybuf = nbuf;
|
||||
if (this.replaybuf.length > 65536) {
|
||||
|
|
@ -211,7 +214,9 @@ function TermSession(gname, pname, wsReq) {
|
|||
var progressdir = path.join("/dgldir/inprogress", this.gname);
|
||||
this.lock = path.join(progressdir, this.pname + ":node:" + ts + ".ttyrec");
|
||||
var lmsg = this.term.pid.toString() + '\n' + this.h + '\n' + this.w + '\n';
|
||||
fs.writeFile(this.lock, lmsg, "utf8");
|
||||
fs.writeFile(this.lock, lmsg, "utf8", function (err) {
|
||||
if (err) tslog("Locking failed: %s", err);
|
||||
});
|
||||
var ttyrec = path.join("/dgldir/ttyrec", this.pname, this.gname,
|
||||
ts + ".ttyrec");
|
||||
this.record = fs.createWriteStream(ttyrec, { mode: 0664 });
|
||||
|
|
@ -239,8 +244,8 @@ TermSession.prototype = new BaseGame();
|
|||
/* Currently this also sends to the player and any watchers. */
|
||||
TermSession.prototype.write_ttyrec = function (datastr) {
|
||||
this.lasttime = new Date();
|
||||
var buf = new Buffer(datastr);
|
||||
var chunk = new Buffer(buf.length + 12);
|
||||
var buf = Buffer.from(datastr);
|
||||
var chunk = Buffer.alloc(buf.length + 12);
|
||||
/* TTYREC headers */
|
||||
chunk.writeUInt32LE(Math.floor(this.lasttime.getTime() / 1000), 0);
|
||||
chunk.writeUInt32LE(1000 * (this.lasttime.getTime() % 1000), 4);
|
||||
|
|
@ -274,7 +279,7 @@ TermSession.prototype.input_msg = function (message) {
|
|||
if (hexstr.length % 2 != 0) {
|
||||
hexstr = hexstr.slice(0, -1);
|
||||
}
|
||||
var keybuf = new Buffer(hexstr, "hex");
|
||||
var keybuf = Buffer.from(hexstr, "hex");
|
||||
this.write(keybuf);
|
||||
}
|
||||
};
|
||||
|
|
@ -287,7 +292,9 @@ TermSession.prototype.close = function () {
|
|||
|
||||
TermSession.prototype.destroy = function () {
|
||||
var tag = this.tag();
|
||||
fs.unlink(this.lock);
|
||||
fs.unlink(this.lock, function (err) {
|
||||
if (err) tslog("Lock removal failed: %s", err);
|
||||
});
|
||||
this.record.end();
|
||||
var watchsocks = this.watchers;
|
||||
this.watchers = [];
|
||||
|
|
@ -356,7 +363,7 @@ DglSession.prototype.startchunk = function () {
|
|||
if (this.reading)
|
||||
return;
|
||||
this.reading = true;
|
||||
var header = new Buffer(12);
|
||||
var header = Buffer.alloc(12);
|
||||
fs.read(this.fd, header, 0, 12, this.rpos, this.datachunk.bind(this));
|
||||
};
|
||||
|
||||
|
|
@ -377,7 +384,7 @@ DglSession.prototype.datachunk = function (err, n, buf) {
|
|||
// Something is probably wrong...
|
||||
tslog("DGL %s: looking for %d bytes", this.tag(), datalen);
|
||||
}
|
||||
var databuf = new Buffer(datalen);
|
||||
var databuf = Buffer.alloc(datalen);
|
||||
fs.read(this.fd, databuf, 0, datalen, this.rpos, this.handledata.bind(this));
|
||||
};
|
||||
|
||||
|
|
@ -394,9 +401,9 @@ DglSession.prototype.handledata = function (err, n, buf) {
|
|||
/* Process the data */
|
||||
this.framepush(buf);
|
||||
var wmsg = JSON.stringify({"t": "d", "d": buf.toString("hex")});
|
||||
for (var i = 0; i < this.watchers.length; i++) {
|
||||
if (this.watchers[i].connected)
|
||||
this.watchers[i].sendUTF(wmsg);
|
||||
for (let watcher of this.watchers) {
|
||||
if (watcher.connected)
|
||||
watcher.sendUTF(wmsg);
|
||||
}
|
||||
this.emit("data", buf);
|
||||
/* Recurse. */
|
||||
|
|
@ -411,6 +418,8 @@ DglSession.prototype.notifier = function (ev, finame) {
|
|||
};
|
||||
|
||||
DglSession.prototype.close = function () {
|
||||
/* The watcher might not be open yet. */
|
||||
if ("recwatcher" in this)
|
||||
this.recwatcher.close();
|
||||
/* Ensure all data is handled before quitting. */
|
||||
this.startchunk();
|
||||
|
|
@ -420,7 +429,9 @@ DglSession.prototype.close = function () {
|
|||
if (connlist[i].connected)
|
||||
connlist[i].close();
|
||||
}
|
||||
fs.close(this.fd);
|
||||
fs.close(this.fd, function (err) {
|
||||
if (err) tslog("PTY close failed: %s", err);
|
||||
});
|
||||
this.emit("close");
|
||||
gamemux.emit('end', this.gname, this.pname);
|
||||
tslog("DGL %s: closed", this.tag());
|
||||
|
|
@ -487,8 +498,11 @@ function checksaved(user, game, callback, args) {
|
|||
var savedirc = game.uname + "save";
|
||||
var basename = String(pwent.uid) + "-" + user + game.suffix;
|
||||
var savefile = path.join("/var/games/roguelike", savedirc, basename);
|
||||
fs.exists(savefile, function (exist) {
|
||||
args.unshift(exist);
|
||||
fs.access(savefile, function (err) {
|
||||
if (err)
|
||||
args.unshift(false);
|
||||
else
|
||||
args.unshift(true);
|
||||
callback.apply(null, args);
|
||||
});
|
||||
}
|
||||
|
|
@ -567,8 +581,8 @@ function bufncmp(buf1, buf2, n) {
|
|||
}
|
||||
|
||||
function isclear(buf) {
|
||||
for (var i = 0; i < clearbufs.length; i++) {
|
||||
if (bufncmp(buf, clearbufs[i], clearbufs[i].length))
|
||||
for (let clearer of clearbufs) {
|
||||
if (bufncmp(buf, clearer, clearer.length))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -688,10 +702,15 @@ function login(req, res, formdata) {
|
|||
function regsetup(username) {
|
||||
function regsetup_l2(err) {
|
||||
for (var g in games) {
|
||||
fs.mkdir(path.join("/dgldir/ttyrec", username, games[g].uname), 0755);
|
||||
fs.mkdir(path.join("/dgldir/ttyrec", username, games[g].uname), 0755,
|
||||
function (err) {
|
||||
if (err) tslog("ttyrec mkdir failed: %s", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
fs.mkdir(path.join("/dgldir/userdata", username), 0755);
|
||||
fs.mkdir(path.join("/dgldir/userdata", username), 0755, function (err) {
|
||||
if (err) tslog("Userdata mkdir failed: %s", err);
|
||||
});
|
||||
fs.mkdir(path.join("/dgldir/ttyrec/", username), 0755, regsetup_l2);
|
||||
}
|
||||
|
||||
|
|
@ -779,7 +798,9 @@ function stopgame(res, formdata) {
|
|||
if (err.code == "ESRCH") {
|
||||
var nodere = RegExp("^" + pname + ":node:");
|
||||
if (fname.match(nodere)) {
|
||||
fs.unlink(fullfile);
|
||||
fs.unlink(fullfile, function (err) {
|
||||
if (err) tslog("Stale lock removal failed: %s", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -832,6 +853,8 @@ function startProgressWatcher() {
|
|||
}
|
||||
|
||||
function serveStatic(req, res, fname) {
|
||||
if (fname[0] !== "/")
|
||||
fname = "/" + fname;
|
||||
var nname = path.normalize(fname);
|
||||
if (nname == "" || nname == "/")
|
||||
nname = "index.html";
|
||||
|
|
@ -839,9 +862,9 @@ function serveStatic(req, res, fname) {
|
|||
path.join(nname, "index.html"); /* it was a directory */
|
||||
var realname = path.join(rlgwebd_options.static_root, nname);
|
||||
var extension = path.extname(realname);
|
||||
fs.exists(realname, function (exists) {
|
||||
fs.access(realname, function (access_err) {
|
||||
var resheaders = {};
|
||||
if (!exists || !extension || extension == ".html")
|
||||
if (access_err || !extension || extension == ".html")
|
||||
resheaders["Content-Type"] = "text/html; charset=utf-8";
|
||||
else if (extension == ".png")
|
||||
resheaders["Content-Type"] = "image/png";
|
||||
|
|
@ -853,7 +876,7 @@ function serveStatic(req, res, fname) {
|
|||
resheaders["Content-Type"] = "image/svg+xml";
|
||||
else
|
||||
resheaders["Content-Type"] = "application/octet-stream";
|
||||
if (exists) {
|
||||
if (!access_err) {
|
||||
fs.readFile(realname, function (error, data) {
|
||||
if (error) {
|
||||
res.writeHead(500, {});
|
||||
|
|
@ -1024,10 +1047,6 @@ function setuinfo(req, res, postdata) {
|
|||
}
|
||||
}
|
||||
|
||||
var errorcodes = [ "Generic Error", "Not logged in", "Invalid data",
|
||||
"Login failed", "Already playing", "Game launch failed",
|
||||
"Server shutting down", "Game not in progress" ];
|
||||
|
||||
function sendError(res, ecode, msg, box) {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
var edict = {"t": "E"};
|
||||
|
|
@ -1264,6 +1283,21 @@ if (rlgwebd_options.use_https) {
|
|||
tls_options.ca = read_or_die(rlgwebd_options.cafile, "CA file");
|
||||
};
|
||||
|
||||
/* Make sure the socket directory is secure. */
|
||||
var socket_dir = path.dirname(rlgwebd_options.control_socket);
|
||||
try {
|
||||
fs.mkdirSync(socket_dir, 0o700);
|
||||
}
|
||||
catch (err) {
|
||||
if (err.code == "EEXIST") {
|
||||
fs.chownSync(socket_dir, 0, 0);
|
||||
fs.chmodSync(socket_dir, 0o700);
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Open the control socket before chrooting where it can't be found */
|
||||
var ctlServer = net.createServer(function (sock) {
|
||||
sock.on('data', consoleHandler);
|
||||
|
|
@ -1288,19 +1322,21 @@ ctlServer.listen(rlgwebd_options.control_socket, function () {
|
|||
tslog("Could not drop permissions: %s", err);
|
||||
process.exit(1);
|
||||
}
|
||||
if (rlgwebd_options.use_https) {
|
||||
httpServer = https.createServer(tls_options, webHandler);
|
||||
httpServer.listen(rlgwebd_options.port);
|
||||
tslog('rlgwebd running on port %d (TLS)', rlgwebd_options.port);
|
||||
wsServer = new WebSocketServer({"httpServer": httpServer});
|
||||
wsServer.on("request", wsHandler);
|
||||
tslog('Secure WebSockets are online');
|
||||
}
|
||||
else {
|
||||
httpServer = http.createServer(webHandler);
|
||||
httpServer.listen(rlgwebd_options.http_port);
|
||||
tslog('rlgwebd running on port %d', rlgwebd_options.http_port);
|
||||
httpServer.listen(rlgwebd_options.port);
|
||||
tslog('rlgwebd running on port %d', rlgwebd_options.port);
|
||||
wsServer = new WebSocketServer({"httpServer": httpServer});
|
||||
wsServer.on("request", wsHandler);
|
||||
tslog('WebSockets are online');
|
||||
if (rlgwebd_options.use_https) {
|
||||
var httpsServer = https.createServer(tls_options, webHandler);
|
||||
httpsServer.listen(rlgwebd_options.https_port);
|
||||
tslog('TLS running on port %d', rlgwebd_options.https_port);
|
||||
var wssServer = new WebSocketServer({"httpServer": httpsServer});
|
||||
wssServer.on("request", wsHandler);
|
||||
tslog('Secure WebSockets are online');
|
||||
}
|
||||
progressWatcher = startProgressWatcher();
|
||||
setInterval(pushStatus, 40000);
|
||||
|
|
|
|||
13
rlgwebd-stop
13
rlgwebd-stop
|
|
@ -1,22 +1,19 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
var net = require('net');
|
||||
var domain = require('domain');
|
||||
var sockpath = "/var/run/rlgwebd.sock";
|
||||
var sockpath = "/var/run/rlgwebd/rlgwebd.sock";
|
||||
|
||||
var dom = domain.create();
|
||||
var sock = new net.Socket();
|
||||
|
||||
dom.on('error', function (err) {
|
||||
sock.on('error', function (err) {
|
||||
console.log("Cannot connect to " + sockpath + ", rlgwebd already stopped.");
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
dom.run(function () {
|
||||
var sock = net.connect(sockpath, function () {
|
||||
sock.on('close', function () {
|
||||
sock.connect(sockpath, function () {
|
||||
sock.on('close', function (had_error) {
|
||||
if (process.argv[2] == "debug")
|
||||
console.log("Control socket closed");
|
||||
});
|
||||
sock.write("quit\n");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,11 +3,9 @@
|
|||
|
||||
# These values are set by default:
|
||||
# Location of the socket for start/stop commands
|
||||
#control_socket = /var/run/rlgwebd.sock
|
||||
#control_socket = /var/run/rlgwebd/rlgwebd.sock
|
||||
# Port number to bind
|
||||
#http_port = 8080
|
||||
# Port number for HTTPS
|
||||
#https_port = 8081
|
||||
#port = 8080
|
||||
# Path to the dgamelaunch installation to chroot into
|
||||
# If you change this, change the Makefile too
|
||||
#chrootDir = /var/dgl/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ After=network.target syslog.target
|
|||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=NODE_PATH=/usr/lib/node_modules
|
||||
Environment=NODE_PATH=/var/local/lib/node_modules
|
||||
ExecStart=/usr/local/bin/rlgwebd
|
||||
ExecStop=/usr/local/bin/rlgwebd-stop
|
||||
Restart=on-failure
|
||||
|
|
|
|||
114
termemu.js
114
termemu.js
|
|
@ -71,6 +71,7 @@ var termemu = {
|
|||
scrB: 0, // init() will set this properly
|
||||
c: null, // Contains cursor position and text attributes
|
||||
offedge: false, // Going off the edge doesn't mean adding a new line
|
||||
lastcode: 0, // Last printed character
|
||||
clearAttrs: function () {
|
||||
/* Make sure to reset ALL attribute properties and NOTHING else. */
|
||||
this.c.bold = false;
|
||||
|
|
@ -460,6 +461,7 @@ var termemu = {
|
|||
this.screen.replaceChild(this.makeRow(), this.screen.childNodes[i]);
|
||||
}
|
||||
this.flipCursor(); // make it appear in the new row
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
},
|
||||
write: function (codes) {
|
||||
|
|
@ -531,6 +533,10 @@ var termemu = {
|
|||
debug(1, "Unrecognized sequence ESC " + codes[i].toString(16));
|
||||
this.comseq = [];
|
||||
}
|
||||
if (this.comseq.length == 0) {
|
||||
// A complete sequence was processed, clear lastcode.
|
||||
this.lastcode = 0;
|
||||
}
|
||||
}
|
||||
else if (this.comseq.length == 2 && this.comseq[0] == 27) {
|
||||
/* An ESC C N sequence. Not implemented. Doesn't check validity
|
||||
|
|
@ -555,6 +561,7 @@ var termemu = {
|
|||
String.fromCharCode(this.comseq[1]) + " 0x" +
|
||||
codes[i].toString(16));
|
||||
this.comseq = [];
|
||||
this.lastcode = 0;
|
||||
}
|
||||
else if (this.comseq[0] == 157) {
|
||||
/* Commands beginning with OSC */
|
||||
|
|
@ -566,6 +573,7 @@ var termemu = {
|
|||
debug(0, "Got " + (this.comseq.length - 1) + "-byte OSC sequence");
|
||||
this.oscProcess();
|
||||
this.comseq = [];
|
||||
this.lastcode = 0;
|
||||
}
|
||||
else
|
||||
this.comseq.push(codes[i]);
|
||||
|
|
@ -582,15 +590,17 @@ var termemu = {
|
|||
/* Chars in csiPre can only occur right after the CSI */
|
||||
debug(1, "Invalid CSI sequence: misplaced prefix");
|
||||
this.comseq = [];
|
||||
this.lastcode = 0;
|
||||
}
|
||||
else
|
||||
this.comseq.push(codes[i]);
|
||||
}
|
||||
else if (csiPost.indexOf(this.comseq[this.comseq.length - 1]) >= 0 &&
|
||||
!csiFinal(codes[i])) {
|
||||
/* Chars is csiPost must come right before the final char */
|
||||
/* Chars in csiPost must come right before the final char */
|
||||
debug(1, "Invalid CSI sequence: misplaced postfix");
|
||||
this.comseq = [];
|
||||
this.lastcode = 0;
|
||||
}
|
||||
else if ((codes[i] >= 48 && codes[i] <= 57) || codes[i] == 59 ||
|
||||
csiPost.indexOf(codes[i]) >= 0) {
|
||||
|
|
@ -605,30 +615,49 @@ var termemu = {
|
|||
else {
|
||||
debug(1, "Invalid CSI sequence: unknown code " + codes[i].toString(16));
|
||||
this.comseq = [];
|
||||
this.lastcode = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
debug(1, "Unknown sequence with " + this.comseq[0].toString(16));
|
||||
this.comseq = [];
|
||||
this.lastcode = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* Treat it as a single character. */
|
||||
if (codes[i] == 5) {
|
||||
else if ((codes[i] >= 32 && codes[i] < 127) || codes[i] >= 160) {
|
||||
/* If it's ASCII, it's printable; take a risk on anything higher */
|
||||
if ((this.c.cset == "0") && (codes[i] in decChars)) {
|
||||
// DEC special character set
|
||||
this.lastcode = decChars[codes[i]];
|
||||
}
|
||||
else {
|
||||
this.lastcode = codes[i];
|
||||
}
|
||||
this.placechar(String.fromCharCode(this.lastcode));
|
||||
}
|
||||
else {
|
||||
/* Treat it as a single control character. */
|
||||
this.singleCtl(codes[i]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
},
|
||||
singleCtl: function (ctlcode) {
|
||||
if (ctlcode == 5) {
|
||||
sendback("06");
|
||||
}
|
||||
else if (codes[i] == 7) {
|
||||
else if (ctlcode == 7) {
|
||||
/* bell */
|
||||
bell(true);
|
||||
}
|
||||
else if (codes[i] == 8) {
|
||||
else if (ctlcode == 8) {
|
||||
/* backspace */
|
||||
if (this.offedge)
|
||||
this.offedge = false;
|
||||
else if (this.c.x > 0)
|
||||
this.cmove(null, this.c.x - 1);
|
||||
}
|
||||
else if (codes[i] == 9) {
|
||||
else if (ctlcode == 9) {
|
||||
/* tab */
|
||||
var xnew;
|
||||
if (this.c.x < this.w - 1) {
|
||||
|
|
@ -641,55 +670,47 @@ var termemu = {
|
|||
this.offedge = true;
|
||||
}
|
||||
}
|
||||
else if (codes[i] >= 10 && codes[i] <= 12) {
|
||||
else if (ctlcode >= 10 && ctlcode <= 12) {
|
||||
/* newline, vertical tab, form feed */
|
||||
if (this.offedge)
|
||||
this.newline(true);
|
||||
else
|
||||
this.newline(false);
|
||||
}
|
||||
else if (codes[i] == 13) {
|
||||
else if (ctlcode == 13) {
|
||||
/* carriage return \r */
|
||||
this.cmove(null, 0);
|
||||
}
|
||||
else if (codes[i] == 14) {
|
||||
else if (ctlcode == 14) {
|
||||
/* shift out */
|
||||
// Currently assuming that G1 is DEC Special & Line Drawing
|
||||
this.c.cset = "0";
|
||||
debug(0, "Using DEC graphics charset.");
|
||||
}
|
||||
else if (codes[i] == 15) {
|
||||
else if (ctlcode == 15) {
|
||||
/* shift in */
|
||||
// Currently assuming that G0 is ASCII
|
||||
this.c.cset = "B";
|
||||
debug(0, "Using ASCII charset.");
|
||||
}
|
||||
else if (codes[i] == 27) {
|
||||
else if (ctlcode == 27) {
|
||||
/* escape */
|
||||
this.comseq.push(codes[i]);
|
||||
}
|
||||
else if (codes[i] < 32 || (codes[i] >= 127 && codes[i] < 160)) {
|
||||
/* Some kind of control character. */
|
||||
debug(1, "Unprintable character 0x" + codes[i].toString(16));
|
||||
this.comseq.push(27);
|
||||
}
|
||||
else {
|
||||
/* If it's ASCII, it's printable; take a risk on anything higher */
|
||||
if ((this.c.cset == "0") && (codes[i] in decChars)) {
|
||||
// DEC special character set
|
||||
this.placechar(String.fromCharCode(decChars[codes[i]]));
|
||||
debug(1, "Unprintable character 0x" + ctlcode.toString(16));
|
||||
}
|
||||
else {
|
||||
this.placechar(String.fromCharCode(codes[i]));
|
||||
if (ctlcode != 27) {
|
||||
// Sequences should preserve lastcode until they are completed
|
||||
this.lastcode = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
},
|
||||
csiProcess: function () {
|
||||
/* Processes the CSI sequence in this.comseq */
|
||||
var c = this.comseq[this.comseq.length - 1];
|
||||
if (this.comseq[0] != 155 || !csiFinal(c))
|
||||
return;
|
||||
var printed = false;
|
||||
var comstr = "";
|
||||
for (var i = 1; i < this.comseq.length; i++)
|
||||
comstr += String.fromCharCode(this.comseq[i]);
|
||||
|
|
@ -698,6 +719,7 @@ var termemu = {
|
|||
var matchCSI = comstr.match(reCSI);
|
||||
if (!matchCSI) {
|
||||
debug(1, "Unrecognized CSI sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
var prefix = null;
|
||||
|
|
@ -725,6 +747,7 @@ var termemu = {
|
|||
/* @ - insert spaces at cursor */
|
||||
if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI @ sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
/* The cursor stays still, but characters move out from under it. */
|
||||
|
|
@ -745,6 +768,7 @@ var termemu = {
|
|||
/* E - next line, F - previous line, G - to column */
|
||||
if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
/* These may be out of range, but cmove will take care of that. */
|
||||
|
|
@ -769,6 +793,7 @@ var termemu = {
|
|||
var y = 0;
|
||||
if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI H sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
if (params[0])
|
||||
|
|
@ -787,6 +812,7 @@ var termemu = {
|
|||
var x = this.c.x;
|
||||
if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI I sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
while (count > 0) {
|
||||
|
|
@ -808,6 +834,7 @@ var termemu = {
|
|||
debug(1, "Warning: CSI ?J not implemented");
|
||||
else if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI J sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
if (!params[0]) {
|
||||
|
|
@ -828,6 +855,7 @@ var termemu = {
|
|||
}
|
||||
else {
|
||||
debug(1, "Unimplemented parameter in CSI J sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
for (var nrow = start; nrow <= end; nrow++) {
|
||||
|
|
@ -855,6 +883,7 @@ var termemu = {
|
|||
debug(1, "Warning: CSI ?K not implemented");
|
||||
else if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI K sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
/* 0 (default): right, 1: left, 2: all. Include cursor position. */
|
||||
|
|
@ -885,11 +914,14 @@ var termemu = {
|
|||
* M - delete current lines */
|
||||
if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
/* CSI LM have no effect outside of the scrolling region */
|
||||
if (this.c.y < this.scrT || this.c.y > this.scrB)
|
||||
if (this.c.y < this.scrT || this.c.y > this.scrB) {
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
this.flipCursor();
|
||||
while (count > 0) {
|
||||
var blankrow = this.makeRow();
|
||||
|
|
@ -915,6 +947,7 @@ var termemu = {
|
|||
/* P - delete at active position, causing cells on the right to shift. */
|
||||
if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI P sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
var cursrow = this.screen.childNodes[this.c.y];
|
||||
|
|
@ -930,6 +963,7 @@ var termemu = {
|
|||
/* S - scroll up, T - scroll down */
|
||||
if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
if (c == 83)
|
||||
|
|
@ -941,6 +975,7 @@ var termemu = {
|
|||
/* X - erase characters */
|
||||
if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
var row = this.screen.childNodes[this.c.y];
|
||||
|
|
@ -954,6 +989,7 @@ var termemu = {
|
|||
var x = this.c.x;
|
||||
if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI Z sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
while (count > 0) {
|
||||
|
|
@ -970,14 +1006,26 @@ var termemu = {
|
|||
/* ` - go to col */
|
||||
if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI ` sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
this.cmove(null, count - 1);
|
||||
}
|
||||
else if (c == 98) {
|
||||
/* b - repeat previous character */
|
||||
if (this.lastcode !== 0) {
|
||||
while (count > 0) {
|
||||
this.placechar(String.fromCharCode(this.lastcode));
|
||||
count--;
|
||||
}
|
||||
printed = true;
|
||||
}
|
||||
}
|
||||
else if (c == 99) {
|
||||
/* c - query terminal attributes */
|
||||
if (prefix !== null) {
|
||||
debug(1, "Unimplemented CSI sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
/* "CSI ? 1 ; 2 c" - VT100 */
|
||||
|
|
@ -987,6 +1035,7 @@ var termemu = {
|
|||
/* d - go to row */
|
||||
if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI d sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
this.cmove(count - 1, null);
|
||||
|
|
@ -997,6 +1046,7 @@ var termemu = {
|
|||
var y = 0;
|
||||
if (prefix || postfix) {
|
||||
debug(1, "Invalid CSI f sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
if (params[0])
|
||||
|
|
@ -1009,6 +1059,7 @@ var termemu = {
|
|||
/* h - set modes */
|
||||
if (prefix != '?') {
|
||||
debug(1, "Unimplemented CSI sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
|
|
@ -1047,6 +1098,7 @@ var termemu = {
|
|||
}
|
||||
else {
|
||||
debug(1, "Unimplemented CSI sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1080,6 +1132,7 @@ var termemu = {
|
|||
/* m - character attributes */
|
||||
if (prefix !== null) {
|
||||
debug(1, "Unimplemented CSI sequence: " + comstr);
|
||||
this.lastcode = 0;
|
||||
return;
|
||||
}
|
||||
if (params.length == 0)
|
||||
|
|
@ -1133,15 +1186,18 @@ var termemu = {
|
|||
t = params[0] - 1;
|
||||
if (params[1] && params[1] <= this.h)
|
||||
b = params[1] - 1;
|
||||
if (b <= t)
|
||||
return;
|
||||
if (b > t) {
|
||||
this.scrT = t;
|
||||
this.scrB = b;
|
||||
this.cmove(0, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
debug(1, "Unimplemented CSI sequence: " + comstr);
|
||||
}
|
||||
if (!printed) {
|
||||
this.lastcode = 0;
|
||||
}
|
||||
return;
|
||||
},
|
||||
oscProcess: function () {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue