mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-26 23:25:53 -05:00
Added xterm terminal and fixed device link.
This commit is contained in:
parent
b6ff01775f
commit
2f37cd882e
@ -1214,8 +1214,9 @@ function createMeshCore(agent) {
|
|||||||
var shell = bash || sh;
|
var shell = bash || sh;
|
||||||
|
|
||||||
var options = { uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: { HISTCONTROL: 'ignoreboth', TERM: 'xterm' } };
|
var options = { uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: { HISTCONTROL: 'ignoreboth', TERM: 'xterm' } };
|
||||||
var setupcommands = ' alias ls=\'ls --color=auto\'\n';
|
var setupcommands = 'alias ls=\'ls --color=auto\'\n';
|
||||||
if (shell == sh) setupcommands += 'stty erase ^H\n'
|
if (shell == sh) setupcommands += 'stty erase ^H\n';
|
||||||
|
setupcommands += 'clear\n';
|
||||||
|
|
||||||
if (script && shell && process.platform == 'linux') {
|
if (script && shell && process.platform == 'linux') {
|
||||||
this.httprequest.process = childProcess.execFile(script, ['script', '--return', '--quiet', '-c', '"' + shell + '"', '/dev/null'], options); // Start as active user
|
this.httprequest.process = childProcess.execFile(script, ['script', '--return', '--quiet', '-c', '"' + shell + '"', '/dev/null'], options); // Start as active user
|
||||||
|
@ -148,34 +148,9 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, au
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
if (typeof e.data == 'object') {
|
|
||||||
var f = new FileReader();
|
|
||||||
if (f.readAsBinaryString) {
|
|
||||||
// Chrome & Firefox (Draft)
|
|
||||||
f.onload = function (e) { obj.xxOnSocketData(e.target.result); }
|
|
||||||
f.readAsBinaryString(new Blob([e.data]));
|
|
||||||
} else if (f.readAsArrayBuffer) {
|
|
||||||
// Chrome & Firefox (Spec)
|
|
||||||
f.onloadend = function (e) { obj.xxOnSocketData(e.target.result); }
|
|
||||||
f.readAsArrayBuffer(e.data);
|
|
||||||
} else {
|
|
||||||
// IE10, readAsBinaryString does not exist, use an alternative.
|
|
||||||
var binary = '';
|
|
||||||
var bytes = new Uint8Array(e.data);
|
|
||||||
var length = bytes.byteLength;
|
|
||||||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
|
||||||
obj.xxOnSocketData(binary);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If we get a string object, it maybe the WebRTC confirm. Ignore it.
|
|
||||||
obj.xxOnSocketData(e.data);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (typeof e.data == 'object') {
|
if (typeof e.data == 'object') {
|
||||||
if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; }
|
if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; }
|
||||||
if (fileReader.readAsBinaryString) {
|
if (fileReader.readAsBinaryString && (obj.m.ProcessBinaryData == null)) {
|
||||||
// Chrome & Firefox (Draft)
|
// Chrome & Firefox (Draft)
|
||||||
fileReaderInuse = true;
|
fileReaderInuse = true;
|
||||||
fileReader.readAsBinaryString(new Blob([e.data]));
|
fileReader.readAsBinaryString(new Blob([e.data]));
|
||||||
@ -209,6 +184,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, au
|
|||||||
obj.xxOnSocketData = function (data) {
|
obj.xxOnSocketData = function (data) {
|
||||||
if (!data || obj.connectstate == -1) return;
|
if (!data || obj.connectstate == -1) return;
|
||||||
if (typeof data === 'object') {
|
if (typeof data === 'object') {
|
||||||
|
if (obj.m.ProcessBinaryData) { return obj.m.ProcessBinaryData(data); }
|
||||||
// This is an ArrayBuffer, convert it to a string array (used in IE)
|
// This is an ArrayBuffer, convert it to a string array (used in IE)
|
||||||
var binary = '', bytes = new Uint8Array(data), length = bytes.byteLength;
|
var binary = '', bytes = new Uint8Array(data), length = bytes.byteLength;
|
||||||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||||||
|
2
public/scripts/xterm-addon-fit.js
Normal file
2
public/scripts/xterm-addon-fit.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(window,function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(){}return e.prototype.activate=function(e){this._terminal=e},e.prototype.dispose=function(){},e.prototype.fit=function(){var e=this.proposeDimensions();if(e&&this._terminal){var t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}},e.prototype.proposeDimensions=function(){if(this._terminal&&this._terminal.element&&this._terminal.element.parentElement){var e=this._terminal._core,t=window.getComputedStyle(this._terminal.element.parentElement),r=parseInt(t.getPropertyValue("height")),n=Math.max(0,parseInt(t.getPropertyValue("width"))),o=window.getComputedStyle(this._terminal.element),i=r-(parseInt(o.getPropertyValue("padding-top"))+parseInt(o.getPropertyValue("padding-bottom"))),a=n-(parseInt(o.getPropertyValue("padding-right"))+parseInt(o.getPropertyValue("padding-left")))-e.viewport.scrollBarWidth;return{cols:Math.max(2,Math.floor(a/e._renderService.dimensions.actualCellWidth)),rows:Math.max(1,Math.floor(i/e._renderService.dimensions.actualCellHeight))}}},e}();t.FitAddon=n}])});
|
||||||
|
//# sourceMappingURL=xterm-addon-fit.js.map
|
2
public/scripts/xterm.js
Normal file
2
public/scripts/xterm.js
Normal file
File diff suppressed because one or more lines are too long
171
public/styles/xterm.css
Normal file
171
public/styles/xterm.css
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
|
||||||
|
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
||||||
|
* https://github.com/chjj/term.js
|
||||||
|
* @license MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* Originally forked from (with the author's permission):
|
||||||
|
* Fabrice Bellard's javascript vt100 for jslinux:
|
||||||
|
* http://bellard.org/jslinux/
|
||||||
|
* Copyright (c) 2011 Fabrice Bellard
|
||||||
|
* The original design remains. The terminal itself
|
||||||
|
* has been extended to include xterm CSI codes, among
|
||||||
|
* other features.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default styles for xterm.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
.xterm {
|
||||||
|
font-feature-settings: "liga" 0;
|
||||||
|
position: relative;
|
||||||
|
user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm.focus,
|
||||||
|
.xterm:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-helpers {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
/**
|
||||||
|
* The z-index of the helpers must be higher than the canvases in order for
|
||||||
|
* IMEs to appear on top.
|
||||||
|
*/
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-helper-textarea {
|
||||||
|
/*
|
||||||
|
* HACK: to fix IE's blinking cursor
|
||||||
|
* Move textarea out of the screen to the far left, so that the cursor is not visible.
|
||||||
|
*/
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
left: -9999em;
|
||||||
|
top: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
z-index: -5;
|
||||||
|
/** Prevent wrapping so the IME appears against the textarea at the correct position */
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .composition-view {
|
||||||
|
/* TODO: Composition position got messed up somewhere */
|
||||||
|
background: #000;
|
||||||
|
color: #FFF;
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .composition-view.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-viewport {
|
||||||
|
/* On OS X this is required in order for the scroll bar to appear fully opaque */
|
||||||
|
background-color: #000;
|
||||||
|
overflow-y: scroll;
|
||||||
|
cursor: default;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-screen {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-screen canvas {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-scroll-area {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm-char-measure-element {
|
||||||
|
display: inline-block;
|
||||||
|
visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -9999em;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm {
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm.enable-mouse-events {
|
||||||
|
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm.xterm-cursor-pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm.column-select.focus {
|
||||||
|
/* Column selection mode */
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-accessibility,
|
||||||
|
.xterm .xterm-message {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 10;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .live-region {
|
||||||
|
position: absolute;
|
||||||
|
left: -9999px;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm-dim {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm-underline {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
@ -27,6 +27,7 @@ var meshCentralSourceFiles = [
|
|||||||
"../views/login-mobile.handlebars",
|
"../views/login-mobile.handlebars",
|
||||||
"../views/terms.handlebars",
|
"../views/terms.handlebars",
|
||||||
"../views/terms-mobile.handlebars",
|
"../views/terms-mobile.handlebars",
|
||||||
|
"../views/xterm.handlebars",
|
||||||
"../views/message.handlebars",
|
"../views/message.handlebars",
|
||||||
"../views/messenger.handlebars",
|
"../views/messenger.handlebars",
|
||||||
"../public/player.htm"
|
"../public/player.htm"
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1770,6 +1770,7 @@
|
|||||||
|
|
||||||
if (xxcurrentView == -1) { if ('{{viewmode}}' != '') { go(parseInt('{{viewmode}}')); } else { setDialogMode(0); go(1); } }
|
if (xxcurrentView == -1) { if ('{{viewmode}}' != '') { go(parseInt('{{viewmode}}')); } else { setDialogMode(0); go(1); } }
|
||||||
if ('{{currentNode}}' != '') { gotoDevice('{{currentNode}}',parseInt('{{viewmode}}'));}
|
if ('{{currentNode}}' != '') { gotoDevice('{{currentNode}}',parseInt('{{viewmode}}'));}
|
||||||
|
else if (args.gotonode != null) { goBackStack.push(1); gotoDevice('node/' + domain + '/' + args.gotonode,parseInt('{{viewmode}}')); delete args.gotonode; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'powertimeline': {
|
case 'powertimeline': {
|
||||||
@ -4785,6 +4786,11 @@
|
|||||||
// Show node last 7 days timeline
|
// Show node last 7 days timeline
|
||||||
masterUpdate(256);
|
masterUpdate(256);
|
||||||
|
|
||||||
|
// Check if we have terminal and file access
|
||||||
|
var terminalAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0));
|
||||||
|
var fileAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0));
|
||||||
|
var amtAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 2048) == 0));
|
||||||
|
|
||||||
// Show bottom buttons
|
// Show bottom buttons
|
||||||
x = '<div class="p10html3right">';
|
x = '<div class="p10html3right">';
|
||||||
if ((meshrights & 4) != 0) {
|
if ((meshrights & 4) != 0) {
|
||||||
@ -4796,7 +4802,8 @@
|
|||||||
x += '<img title=\"' + "Place link to this device in the clipboard" + '\" src="images/link1.png" style=cursor:pointer onclick=p10deviceLinkToClipboard() /> ';
|
x += '<img title=\"' + "Place link to this device in the clipboard" + '\" src="images/link1.png" style=cursor:pointer onclick=p10deviceLinkToClipboard() /> ';
|
||||||
if (mesh.mtype == 2) x += '<a href=# onclick=p10showNodeNetInfoDialog("' + node._id + '") title=\"' + "Show device network interface information" + '\">' + "Interfaces" + '</a> ';
|
if (mesh.mtype == 2) x += '<a href=# onclick=p10showNodeNetInfoDialog("' + node._id + '") title=\"' + "Show device network interface information" + '\">' + "Interfaces" + '</a> ';
|
||||||
if (xxmap != null) x += '<a href=# onclick=p10showNodeLocationDialog("' + node._id + '") title=\"' + "Show device locations information" + '\">' + "Location" + '</a> ';
|
if (xxmap != null) x += '<a href=# onclick=p10showNodeLocationDialog("' + node._id + '") title=\"' + "Show device locations information" + '\">' + "Location" + '</a> ';
|
||||||
if (((meshrights & 8) != 0) && (mesh.mtype == 2)) x += '<a href=# onclick=p10showMeshCmdDialog(1,"' + node._id + '") title=\"' + "Traffic router used to connect to a device thru this server" + '.\">' + "Router" + '</a> ';
|
if ((terminalAccess) && ((meshrights & 8) != 0) && (mesh.mtype == 2)) x += '<a href=# onclick=p10showMeshCmdDialog(1,"' + node._id + '") title=\"' + "Traffic router used to connect to a device thru this server" + '.\">' + "Router" + '</a> ';
|
||||||
|
if ((mesh.mtype == 2) && ((node.agent.caps & 2) != 0) && ((meshrights & 8) != 0) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0))) { x += '<a href=# onclick=p10openxterm(event,"' + node._id + '") title=\"' + "Open XTerm terminal" + '\">' + "XTerm" + '</a> '; }
|
||||||
|
|
||||||
// RDP link, show this link only of the remote machine is Windows.
|
// RDP link, show this link only of the remote machine is Windows.
|
||||||
if (((connectivity & 1) != 0) && (clickOnce == true) && (mesh.mtype == 2) && ((meshrights & 8) != 0)) {
|
if (((connectivity & 1) != 0) && (clickOnce == true) && (mesh.mtype == 2) && ((meshrights & 8) != 0)) {
|
||||||
@ -4827,11 +4834,6 @@
|
|||||||
Q('MainComputerImage').setAttribute('src', 'images/icons256-' + node.icon + '-1.png');
|
Q('MainComputerImage').setAttribute('src', 'images/icons256-' + node.icon + '-1.png');
|
||||||
Q('MainComputerImage').className = ((!node.conn) || (node.conn == 0)?'gray':'');
|
Q('MainComputerImage').className = ((!node.conn) || (node.conn == 0)?'gray':'');
|
||||||
|
|
||||||
// Check if we have terminal and file access
|
|
||||||
var terminalAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0));
|
|
||||||
var fileAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0));
|
|
||||||
var amtAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 2048) == 0));
|
|
||||||
|
|
||||||
// Setup/Refresh the desktop tab
|
// Setup/Refresh the desktop tab
|
||||||
if (terminalAccess) { setupTerminal(); }
|
if (terminalAccess) { setupTerminal(); }
|
||||||
if (fileAccess) { setupFiles(); }
|
if (fileAccess) { setupFiles(); }
|
||||||
@ -5262,7 +5264,18 @@
|
|||||||
function p10showMqttLoginDialog(nodeid) { meshserver.send({ action: 'getmqttlogin', nodeid: nodeid }); }
|
function p10showMqttLoginDialog(nodeid) { meshserver.send({ action: 'getmqttlogin', nodeid: nodeid }); }
|
||||||
|
|
||||||
// Place a device link URL in the clipboard
|
// Place a device link URL in the clipboard
|
||||||
function p10deviceLinkToClipboard() { copyTextToClip2(document.URL.split('?')[0].split('#')[0] + '?node=' + currentNode._id.split('/')[2] + '&viewmode=10'); }
|
function p10deviceLinkToClipboard() { copyTextToClip2(document.URL.split('?')[0].split('#')[0] + '?gotonode=' + currentNode._id.split('/')[2] + '&viewmode=10'); }
|
||||||
|
|
||||||
|
// Open XTerm
|
||||||
|
function p10openxterm(e, nodeid) {
|
||||||
|
haltEvent(e);
|
||||||
|
var url = '/xterm?nodeid=' + encodeURIComponent(nodeid) + '&auto=1';
|
||||||
|
var node = getNodeFromId(nodeid);
|
||||||
|
if (node == null) return;
|
||||||
|
if ([1, 2, 3, 4, 21, 22].indexOf(node.agent.id) >= 0) { url += '&fixsize=1'; }
|
||||||
|
window.open(url, 'xterm:' + nodeid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Show MeshCmd dialog
|
// Show MeshCmd dialog
|
||||||
function p10showMeshCmdDialog(mode, nodeid) {
|
function p10showMeshCmdDialog(mode, nodeid) {
|
||||||
|
227
views/xterm.handlebars
Normal file
227
views/xterm.handlebars
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
||||||
|
<meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="format-detection" content="telephone=no" />
|
||||||
|
<link type="text/css" href="styles/style.css" media="screen" rel="stylesheet" title="CSS" />
|
||||||
|
<link type="text/css" href="styles/xterm.css" media="screen" rel="stylesheet" title="CSS" />
|
||||||
|
<script type="text/javascript" src="scripts/common-0.0.1.js"></script>
|
||||||
|
<script type="text/javascript" src="scripts/meshcentral.js"></script>
|
||||||
|
<script type="text/javascript" src="scripts/agent-redir-ws-0.1.1.js"></script>
|
||||||
|
<script type="text/javascript" src="scripts/agent-redir-rtc-0.1.0.js"></script>
|
||||||
|
<script type="text/javascript" src="scripts/xterm.js"></script>
|
||||||
|
<script type="text/javascript" src="scripts/xterm-addon-fit.js"></script>
|
||||||
|
<title>{{{name}}}</title>
|
||||||
|
</head>
|
||||||
|
<body style="overflow:hidden;background-color:black">
|
||||||
|
<div id=p11 class="noselect" style="overflow:hidden">
|
||||||
|
<div id=deskarea0>
|
||||||
|
<div id=deskarea1 class="areaHead">
|
||||||
|
<div class="toright2">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input id="ConnectButton" style="display:none" type=button value="Connect" onclick="connectButton()">
|
||||||
|
<input id="DisconnectButton" type=button value="Disconnect" onclick="connectButton()">
|
||||||
|
<span><b>{{{name}}}</b></span> - <span id="termstatus"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id=deskarea2 style="">
|
||||||
|
<div class="areaProgress"><div id="progressbar" style=""></div></div>
|
||||||
|
</div>
|
||||||
|
<div id=deskarea3x style="max-height:calc(100vh - 54px);height:calc(100vh - 54px);">
|
||||||
|
<div id="bigok" style="display:none;left:calc((100vh / 2))"><b>✓</b></div>
|
||||||
|
<div id="bigfail" style="display:none;left:calc((100vh / 2))"><b>✗</b></div>
|
||||||
|
<div id="metadatadiv" style="padding:20px;color:lightgrey;text-align:left;display:none"></div>
|
||||||
|
<div id=terminal style="max-height:calc(100vh - 54px);height:calc(100vh - 54px);"></div>
|
||||||
|
<div id=p11DeskConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:17px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=clearConsoleMsg()></div>
|
||||||
|
</div>
|
||||||
|
<div id=deskarea4 class="areaHead">
|
||||||
|
<div class="toright2">
|
||||||
|
</div>
|
||||||
|
<div style="height:21px;max-height:21px">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id=dialog class="noselect" style="display:none">
|
||||||
|
<div id=dialogHeader>
|
||||||
|
<div tabindex=0 id=id_dialogclose onclick=setDialogMode() onkeypress="if (event.key == 'Enter') setDialogMode()">✖</div>
|
||||||
|
<div id=id_dialogtitle></div>
|
||||||
|
</div>
|
||||||
|
<div id=dialogBody>
|
||||||
|
<div id=dialog1>
|
||||||
|
<div id=id_dialogMessage style=""></div>
|
||||||
|
</div>
|
||||||
|
<div id=dialog2 style="">
|
||||||
|
<div id=id_dialogOptions></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="idx_dlgButtonBar">
|
||||||
|
<input id="idx_dlgCancelButton" type="button" value="Cancel" style="" onclick="dialogclose(0)">
|
||||||
|
<input id="idx_dlgOkButton" type="button" value="OK" style="" onclick="dialogclose(1)">
|
||||||
|
<div><input id="idx_dlgDeleteButton" type="button" value="Delete" style="display:none" onclick="dialogclose(2)"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
var term = null;
|
||||||
|
var termfit = null;
|
||||||
|
var tunnel = null;
|
||||||
|
var domain = '{{{domain}}}';
|
||||||
|
var domainUrl = '{{{domainurl}}}';
|
||||||
|
var authCookie = '{{{authCookie}}}';
|
||||||
|
var authRelayCookie = '{{{authRelayCookie}}}';
|
||||||
|
var serverPublicNamePort = '{{{serverDnsName}}}:{{{serverPublicPort}}}';
|
||||||
|
var StatusStrs = ["Disconnected", "Connecting...", "Setup...", "Connected", "Intel® AMT Connected"];
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
// Parse any URL arguments
|
||||||
|
args = parseUriArgs();
|
||||||
|
|
||||||
|
// Connect to the mesh server
|
||||||
|
meshserver = MeshServerCreateControl(domainUrl, authCookie);
|
||||||
|
meshserver.onStateChanged = onStateChanged;
|
||||||
|
meshserver.onMessage = onMessage;
|
||||||
|
meshserver.trace = (args.trace == 1);
|
||||||
|
meshserver.Start();
|
||||||
|
|
||||||
|
// When the user resizes the window, re-fit
|
||||||
|
window.onresize = function () { if (termfit != null) { termfit.fit(); } }
|
||||||
|
|
||||||
|
// Update the terminal status and buttons
|
||||||
|
QH('termstatus', StatusStrs[0]);
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the correct button state
|
||||||
|
function updateButtons() {
|
||||||
|
var tunnelState = ((tunnel != null) && (tunnel.state != 0) && (meshserver.State == 2));
|
||||||
|
QE('ConnectButton', meshserver.State == 2);
|
||||||
|
QV('ConnectButton', !tunnelState);
|
||||||
|
QV('DisconnectButton', tunnelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeshServer - Change State
|
||||||
|
function onStateChanged(server, state, prevState, errorCode) {
|
||||||
|
QE('ConnectButton', (state == 2));
|
||||||
|
if ((state == 2) && (args.auto == 1)) { delete args.auto; connectButton(); }
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeshServer - Handle messages
|
||||||
|
function onMessage(server, message) { }
|
||||||
|
|
||||||
|
// Handles a tunnel to a remote shell
|
||||||
|
function CreateRemoteTunnel(onTunnelUpdate) {
|
||||||
|
var obj = { protocol: 1 };
|
||||||
|
obj.onTunnelUpdate = onTunnelUpdate;
|
||||||
|
obj.xxStateChange = function (state) { }
|
||||||
|
obj.ProcessBinaryData = function (data) { obj.onTunnelUpdate(data); }
|
||||||
|
obj.ProcessData = function (data) { obj.onTunnelUpdate(data); }
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the connect/disconnect button is pressed
|
||||||
|
function connectButton() {
|
||||||
|
if (!tunnel) {
|
||||||
|
// Setup the terminal with auto-fit
|
||||||
|
if (term != null) { term.dispose(); }
|
||||||
|
if (args.fixsize != 1) { termfit = new FitAddon.FitAddon(); }
|
||||||
|
term = new Terminal();
|
||||||
|
if (termfit) { term.loadAddon(termfit); }
|
||||||
|
term.open(Q('terminal'));
|
||||||
|
term.onData(function (data) { if (tunnel != null) { tunnel.sendText(data); } })
|
||||||
|
term.onResize(function (size) {
|
||||||
|
//console.log('Resize', size);
|
||||||
|
//term.resize(size.cols, size.rows);
|
||||||
|
//if (tunnel != null) { tunnel.send(String.fromCharCode(27) + '[8;' + size.cols + ';' + size.rows + 't') }
|
||||||
|
});
|
||||||
|
if (termfit) { termfit.fit(); }
|
||||||
|
|
||||||
|
// Setup a terminal tunnel to the agent
|
||||||
|
tunnel = CreateAgentRedirect(meshserver, CreateRemoteTunnel(tunnelUpdate), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
||||||
|
tunnel.Start(args.nodeid);
|
||||||
|
tunnel.onStateChanged = onTunnelStateChange;
|
||||||
|
} else {
|
||||||
|
tunnel.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tunnelUpdate(data) { if (typeof data == 'string') { term.writeUtf8(data); } else { term.writeUtf8(new Uint8Array(data)); } }
|
||||||
|
|
||||||
|
// Called when the terminal state changes
|
||||||
|
function onTunnelStateChange(xterminal, state) {
|
||||||
|
var xstate = state;
|
||||||
|
if ((xstate == 3) && (xterminal.contype == 2)) { xstate++; }
|
||||||
|
var str = StatusStrs[xstate];
|
||||||
|
if (tunnel.webRtcActive == true) { str += ", WebRTC"; }
|
||||||
|
QH('termstatus', str);
|
||||||
|
switch (state) {
|
||||||
|
case 0:
|
||||||
|
// Disconnected, clear the terminal
|
||||||
|
term.dispose();
|
||||||
|
term = null;
|
||||||
|
termfit = null;
|
||||||
|
tunnel = null;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
// Connected
|
||||||
|
term.focus();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Other
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POPUP DIALOG
|
||||||
|
//
|
||||||
|
|
||||||
|
// null = Hidden, 1 = Generic Message
|
||||||
|
var xxdialogMode;
|
||||||
|
var xxdialogFunc;
|
||||||
|
var xxdialogButtons;
|
||||||
|
var xxdialogTag;
|
||||||
|
var xxcurrentView = -1;
|
||||||
|
|
||||||
|
// Display a dialog box
|
||||||
|
// Parameters: Dialog Mode (0 = none), Dialog Title, Buttons (1 = OK, 2 = Cancel, 3 = OK & Cancel), Call back function(0 = Cancel, 1 = OK), Dialog Content (Mode 2 only)
|
||||||
|
function setDialogMode(x, y, b, f, c, tag) {
|
||||||
|
xxdialogMode = x;
|
||||||
|
xxdialogFunc = f;
|
||||||
|
xxdialogButtons = b;
|
||||||
|
xxdialogTag = tag;
|
||||||
|
QE('idx_dlgOkButton', true);
|
||||||
|
QV('idx_dlgOkButton', b & 1);
|
||||||
|
QV('idx_dlgCancelButton', b & 2);
|
||||||
|
QV('id_dialogclose', (b & 2) || (b & 8));
|
||||||
|
QV('idx_dlgDeleteButton', b & 4);
|
||||||
|
QV('idx_dlgButtonBar', b & 7);
|
||||||
|
if (y) QH('id_dialogtitle', y);
|
||||||
|
for (var i = 1; i < 3; i++) { QV('dialog' + i, i == x); } // Edit this line when more dialogs are added
|
||||||
|
QV('dialog', x);
|
||||||
|
if (c) { if (x == 2) { QH('id_dialogOptions', c); } else { QH('id_dialogMessage', c); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the dialog box must be closed
|
||||||
|
function dialogclose(x) {
|
||||||
|
var f = xxdialogFunc, b = xxdialogButtons, t = xxdialogTag;
|
||||||
|
setDialogMode();
|
||||||
|
if (((b & 8) || x) && f) f(x, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
function messagebox(t, m) { setSessionActivity(); QH('id_dialogMessage', m); setDialogMode(1, t, 1); }
|
||||||
|
function statusbox(t, m) { setSessionActivity(); QH('id_dialogMessage', m); setDialogMode(1, t); }
|
||||||
|
function haltEvent(e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||||||
|
function pad2(num) { var s = '00' + num; return s.substr(s.length - 2); }
|
||||||
|
function format(format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function (match, number) { return typeof args[number] != 'undefined' ? args[number] : match; }); };
|
||||||
|
function parseUriArgs() { var name, r = {}, parsedUri = window.document.location.href.split(/[\?&|\=]/); parsedUri.splice(0, 1); for (x in parsedUri) { switch (x % 2) { case 0: { name = decodeURIComponent(parsedUri[x]); break; } case 1: { r[name] = decodeURIComponent(parsedUri[x]); var x = parseInt(r[name]); if (x == r[name]) { r[name] = x; } break; } default: { break; } } } return r; }
|
||||||
|
|
||||||
|
start();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
34
webserver.js
34
webserver.js
@ -1695,6 +1695,39 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serve the xterm page
|
||||||
|
function handleXTermRequest(req, res) {
|
||||||
|
const domain = checkUserIpAddress(req, res);
|
||||||
|
if (domain == null) { parent.debug('web', 'handleXTermRequest: Bad domain'); res.sendStatus(404); return; }
|
||||||
|
if ((domain.loginkey != null) && (domain.loginkey.indexOf(req.query.key) == -1)) { res.sendStatus(404); return; } // Check 3FA URL key
|
||||||
|
|
||||||
|
parent.debug('web', 'handleXTermRequest: sending xterm');
|
||||||
|
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' });
|
||||||
|
if (req.session && req.session.userid) {
|
||||||
|
if (req.session.domainid != domain.id) { res.redirect(domain.url + getQueryPortion(req)); return; } // Check if the session is for the correct domain
|
||||||
|
var user = obj.users[req.session.userid];
|
||||||
|
if ((user == null) || (req.query.nodeid == null)) { res.redirect(domain.url + getQueryPortion(req)); return; } // Check if the user exists
|
||||||
|
|
||||||
|
// Check permissions
|
||||||
|
obj.GetNodeWithRights(domain, user, req.query.nodeid, function (node, rights, visible) {
|
||||||
|
if ((node == null) || ((rights & 8) == 0) || ((rights != 0xFFFFFFFF) && ((rights & 512) != 0))) { res.redirect(domain.url + getQueryPortion(req)); return; }
|
||||||
|
|
||||||
|
var logoutcontrols = { name: user.name };
|
||||||
|
var extras = (req.query.key != null) ? ('&key=' + req.query.key) : '';
|
||||||
|
if ((domain.ldap == null) && (domain.sspi == null) && (obj.args.user == null) && (obj.args.nousers != true)) { logoutcontrols.logoutUrl = (domain.url + 'logout?' + Math.random() + extras); } // If a default user is in use or no user mode, don't display the logout button
|
||||||
|
|
||||||
|
// Create a authentication cookie
|
||||||
|
const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id, ip: cleanRemoteAddr(req.ip) }, obj.parent.loginCookieEncryptionKey);
|
||||||
|
const authRelayCookie = obj.parent.encodeCookie({ ruserid: user._id, domainid: domain.id }, obj.parent.loginCookieEncryptionKey);
|
||||||
|
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
|
||||||
|
render(req, res, getRenderPage('xterm', req), getRenderArgs({ serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, authCookie: authCookie, authRelayCookie: authRelayCookie, logoutControls: JSON.stringify(logoutcontrols), name: EscapeHtml(node.name) }, domain));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.redirect(domain.url + getQueryPortion(req));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Render the terms of service.
|
// Render the terms of service.
|
||||||
function handleTermsRequest(req, res) {
|
function handleTermsRequest(req, res) {
|
||||||
const domain = checkUserIpAddress(req, res);
|
const domain = checkUserIpAddress(req, res);
|
||||||
@ -3447,6 +3480,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||||||
obj.app.get(url + 'backup.zip', handleBackupRequest);
|
obj.app.get(url + 'backup.zip', handleBackupRequest);
|
||||||
obj.app.post(url + 'restoreserver.ashx', handleRestoreRequest);
|
obj.app.post(url + 'restoreserver.ashx', handleRestoreRequest);
|
||||||
obj.app.get(url + 'terms', handleTermsRequest);
|
obj.app.get(url + 'terms', handleTermsRequest);
|
||||||
|
obj.app.get(url + 'xterm', handleXTermRequest);
|
||||||
obj.app.post(url + 'login', handleLoginRequest);
|
obj.app.post(url + 'login', handleLoginRequest);
|
||||||
obj.app.post(url + 'tokenlogin', handleLoginRequest);
|
obj.app.post(url + 'tokenlogin', handleLoginRequest);
|
||||||
obj.app.get(url + 'logout', handleLogoutRequest);
|
obj.app.get(url + 'logout', handleLogoutRequest);
|
||||||
|
Loading…
Reference in New Issue
Block a user