508 lines
16 KiB
JavaScript
508 lines
16 KiB
JavaScript
"use strict";
|
|
|
|
function Socket(options) {
|
|
function getStringFromData(buf, begin, len, defaultText) {
|
|
var szValue;
|
|
if ("function" == typeof TextDecoder) szValue = new TextDecoder("utf-8").decode(new Uint8Array(buf.buffer, buf.byteOffset + begin, len)); else try {
|
|
szValue = String.fromCharCode.apply(null, new Uint8Array(buf.buffer, buf.byteOffset + begin, len)),
|
|
szValue = decodeURIComponent(escape(szValue)).replace(/\0/g, "");
|
|
} catch (ex) {
|
|
szValue = "";
|
|
}
|
|
return szValue.length > 0 ? szValue : defaultText;
|
|
}
|
|
function getPostCodeFromData(buf) {
|
|
var index1, index2, szValue = "";
|
|
return buf.length >= 2 && (index1 = ("00" + buf[1].toString(16).toUpperCase()).substr(-2),
|
|
index2 = ("00" + buf[0].toString(16).toUpperCase()).substr(-2), szValue = index1 + index2),
|
|
szValue;
|
|
}
|
|
function IRC_CMD(dataView) {
|
|
this.cmd, this.size, this.seq, this.flags, this.buf;
|
|
try {
|
|
dataView && (this.cmd = dataView.getUint32(0, !0), this.size = dataView.getUint32(4, !0),
|
|
this.seq = dataView.getUint16(8, !0), this.flags = dataView.getUint16(10, !0), this.buf = new Uint8Array(dataView.buffer, dataView.byteOffset + 12));
|
|
} catch (e) {}
|
|
}
|
|
function IRC_SERVER_HELLO(dataView) {
|
|
this.iv, this.status, this.flags, this.session_key;
|
|
try {
|
|
dataView && (4 === me.version ? this.status = dataView.getUint8(0, !0) : (this.status = dataView.getUint8(16),
|
|
this.flags = dataView.getUint32(17, !0)));
|
|
} catch (e) {}
|
|
}
|
|
function Handshake(wsuri, sessionKey, channel, openCommand, success, failure) {
|
|
var socket, onfail = failure || function(sock, reason) {
|
|
console.log("Handshake failed: " + reason);
|
|
}, onsuccess = success || function(sock) {
|
|
sock.onmessage = function(evt) {};
|
|
}, sockopen_auth = function(evt) {
|
|
if (4 === me.version) return !0;
|
|
var client_hello = new Uint8Array(54);
|
|
client_hello[16] = openCommand, client_hello[17] = channel;
|
|
for (var i = 0; i < sessionKey.length; i++) client_hello[22 + i] = sessionKey.charCodeAt(i);
|
|
socket.send(client_hello);
|
|
}, sockrecv_auth = function(evt) {
|
|
var server_hello = new IRC_SERVER_HELLO(new DataView(evt.data));
|
|
if (4 === me.version) switch (server_hello.status) {
|
|
case DVCNET.CMD_AUTHENTICATE:
|
|
var name = channel === CMD_CHANNEL ? "CMD" : "KVM", client_hello = new Uint8Array(34);
|
|
client_hello[0] = "CMD" === name ? 2 : 1, client_hello[1] = 32;
|
|
for (var i = 0; i < sessionKey.length; i++) client_hello[2 + i] = sessionKey.charCodeAt(i);
|
|
socket.send(client_hello);
|
|
break;
|
|
|
|
case DVCNET.CMD_AUTHENTICATED:
|
|
socket.onmessage = null, socket.onopen = null, onsuccess(socket);
|
|
break;
|
|
|
|
case DVCNET.NEG_BUSY_NO_MURC:
|
|
video.set_licensed_murc(!1);
|
|
|
|
case DVCNET.NEG_BUSY:
|
|
me.cmdsocket = socket;
|
|
var msgKey = "IRC.message.ConsoleInUseNoShare", tmrMsgKey = "IRC.message.TimedAutoDisconnect", msgBox = {
|
|
callback: "neg_seize",
|
|
messageKey: msgKey,
|
|
stayTime: 10,
|
|
timerStringKey: tmrMsgKey,
|
|
title: null,
|
|
icon: null,
|
|
buttons: [ {
|
|
text: "Acquire",
|
|
textKey: "IRC.control.Acquire",
|
|
isDefault: !0,
|
|
value: !0
|
|
}, {
|
|
text: "Cancel",
|
|
textKey: "cancel",
|
|
isDefault: !1,
|
|
value: !1
|
|
} ]
|
|
};
|
|
video.confirm(msgBox);
|
|
break;
|
|
|
|
case DVCNET.NEG_SEIZE:
|
|
case DVCNET.NEG_SHARE:
|
|
onfail(socket, server_hello.status);
|
|
break;
|
|
|
|
case DVCNET.CMD_DENIED:
|
|
case DVCNET.NEG_DROP:
|
|
case DVCNET.CMD_NO_SESSIONS:
|
|
default:
|
|
socket.onmessage = null, socket.onopen = null, onfail(socket, server_hello.status);
|
|
} else switch (socket.onmessage = null, socket.onopen = null, server_hello.status) {
|
|
case 0:
|
|
onsuccess(socket);
|
|
break;
|
|
|
|
default:
|
|
onfail(socket, server_hello.status);
|
|
}
|
|
};
|
|
if ("function" == typeof MozWebSocket) socket = new MozWebSocket(wsuri); else {
|
|
if ("function" != typeof WebSocket) throw "No WebSockets!";
|
|
try {
|
|
socket = new WebSocket(wsuri);
|
|
} catch (e) {
|
|
"SyntaxError" === e.message && this.ie && wsuri.match(/[^[\]]+(?=])/g).length ? onfail(null, "IE+IPv6") : onfail(null, e.message);
|
|
}
|
|
}
|
|
socket && (socket.onerror = function(evt) {
|
|
1 === socket.readyState ? console.log("WebSocket connection failed: " + evt.type) : console.log("WebSocket error: " + evt.type);
|
|
}, socket.binaryType = "arraybuffer", socket.onmessage = sockrecv_auth, socket.onopen = sockopen_auth);
|
|
}
|
|
var me = this, KVM_CHANNEL = 0, CMD_CHANNEL = 1, DVCNET = {
|
|
CMD_AUTHENTICATE: 80,
|
|
CMD_DENIED: 81,
|
|
CMD_AUTHENTICATED: 82,
|
|
NEG_BUSY: 83,
|
|
NEG_DROP: 84,
|
|
NEG_SEIZE: 85,
|
|
NEG_SHARE: 86,
|
|
CMD_NO_SESSIONS: 88,
|
|
NEG_BUSY_NO_MURC: 89
|
|
}, SCSI_ERR = {
|
|
APPLET_CONNECT: -2,
|
|
NO_IMAGE: -3,
|
|
OPTION_ERROR: -4,
|
|
SCRIPT_CONNECT: -5,
|
|
NO_LICENSE: -6,
|
|
REJECTED: -7,
|
|
CONNECT_FAILED: -8,
|
|
OP_FAILED: -9,
|
|
EXPECTED_ISO: -10,
|
|
EXPECTED_IMG: -11,
|
|
VIDEO_STARTUP: -12,
|
|
FW_ACTIVE: -13,
|
|
FLASH_FAILED: -14
|
|
};
|
|
this.onstatuschange = function() {}, this.version = options.version || 4, this.openCommand = options.openCommand || 0,
|
|
this.sessionKey = options.sessionKey, this.sockaddr = "wss://" + options.host + "/wss/ircport",
|
|
this.sockrecv = options.sockrecv, this.ie = options.ie, this.video = options.video,
|
|
this.health_state = "", this.power_state = "", this.neg_seize = function(resp) {
|
|
if (me.openCommand = resp ? 1 : 0, resp === !0) {
|
|
var cmd = new Uint8Array(2);
|
|
cmd[0] = DVCNET.NEG_SEIZE, cmd[1] = 0, me.cmdsocket.send(cmd);
|
|
} else me.close(), me.onclose && me.onclose();
|
|
}, this.handleSeize = function(irc_cmd) {
|
|
var user = getStringFromData(irc_cmd.buf, 0, 64, "UNKNOWN"), addr = getStringFromData(irc_cmd.buf, 64, 64, "UNKNOWN"), dlgMsg = "IRC.message.RequestAcquire", tmrMsg = "IRC.message.AutoPermission", actMsg = "IRC.status.granted", messageInserts = [ {
|
|
placeHolder: "{0}",
|
|
value: user
|
|
}, {
|
|
placeHolder: "{1}",
|
|
value: addr
|
|
} ], msgBox = {
|
|
callback: "send_bool",
|
|
retvalOverride: !0,
|
|
messageKey: dlgMsg,
|
|
messageInserts: messageInserts,
|
|
title: null,
|
|
icon: null,
|
|
stayTime: 10,
|
|
timerStringKey: tmrMsg,
|
|
actionKey: actMsg,
|
|
buttons: [ {
|
|
textKey: "IRC.control.Yes",
|
|
isDefault: !0,
|
|
value: !0
|
|
}, {
|
|
textKey: "IRC.control.No",
|
|
value: !1
|
|
} ]
|
|
};
|
|
video.confirm(msgBox);
|
|
}, this.handleShare = function(irc_cmd) {
|
|
var user = getStringFromData(irc_cmd.buf, 0, 64, "UNKNOWN"), addr = getStringFromData(irc_cmd.buf, 64, 64, "UNKNOWN");
|
|
me.sendBool(!1), video.handle_share(user, addr);
|
|
}, this.handlePostCode = function(irc_cmd) {
|
|
if (2 === irc_cmd.size) {
|
|
var code = getPostCodeFromData(irc_cmd.buf);
|
|
video.post_code(code);
|
|
}
|
|
}, this.handleAckNotification = function(irc_cmd) {
|
|
var dlgMsg = "";
|
|
if (4 !== irc_cmd.size) return void $.log("IRC: rejecting Ack notification: size is not 4.");
|
|
var dataView = new DataView(irc_cmd.buf.buffer, irc_cmd.buf.byteOffset), result = dataView.getInt8(0, !0), device = dataView.getInt8(1, !0), type = dataView.getInt8(2, !0), connected = 0 !== dataView.getInt8(3, !0) ? !0 : !1;
|
|
if (0 === result) video.vm_change({
|
|
isFloppy: 1 == device,
|
|
isURL: 8 == type,
|
|
isConnected: !!connected
|
|
}); else if (0 > result && !connected) {
|
|
switch (result) {
|
|
case -1:
|
|
dlgMsg = "IRC.message.CannotParse";
|
|
break;
|
|
|
|
case SCSI_ERR.APPLET_CONNECT:
|
|
case SCSI_ERR.SCRIPT_CONNECT:
|
|
dlgMsg = "IRC.message.DriveAlreadyConnected";
|
|
break;
|
|
|
|
case SCSI_ERR.NO_IMAGE:
|
|
dlgMsg = "IRC.message.ImageNotFound";
|
|
break;
|
|
|
|
case SCSI_ERR.OPTION_ERROR:
|
|
dlgMsg = "IRC.message.InternalConfigError";
|
|
break;
|
|
|
|
case SCSI_ERR.NO_LICENSE:
|
|
dlgMsg = "IRC.message.VMNotLicensed";
|
|
break;
|
|
|
|
case SCSI_ERR.REJECTED:
|
|
dlgMsg = "IRC.message.CannotConnect";
|
|
break;
|
|
|
|
default:
|
|
dlgMsg = "IRC.message.UnknownError";
|
|
}
|
|
if (dlgMsg.length > 0) {
|
|
var msgBox = {
|
|
messageKey: dlgMsg,
|
|
stayTime: 10,
|
|
title: null,
|
|
icon: null
|
|
};
|
|
video.alert(msgBox);
|
|
}
|
|
}
|
|
}, this.handleFirmwareUpdate = function(irc_cmd) {
|
|
var dlgMsg = "IRC.message.ClosedFwUpgrade", msgBox = {
|
|
messageKey: dlgMsg,
|
|
stayTime: 20,
|
|
title: null,
|
|
icon: null
|
|
};
|
|
video.alert(msgBox), me.close();
|
|
}, this.handleUnauthorized = function(irc_cmd) {
|
|
var priv = "";
|
|
switch (irc_cmd.flags) {
|
|
case 2:
|
|
priv = "RemoteConsolePriv";
|
|
break;
|
|
|
|
case 3:
|
|
priv = "VirtualMediaPriv";
|
|
break;
|
|
|
|
case 4:
|
|
priv = "VirtualPowerAndResetPriv";
|
|
}
|
|
priv.length > 0 && video.unauthorized(priv);
|
|
}, this.handleLicenseViolation = function(irc_cmd) {
|
|
var dlgMsg = "IRC.message.IRCNotLicensed", msgBox = {
|
|
titleKey: "IRC.title.LicenseRequired",
|
|
type: "critical",
|
|
messageKey: dlgMsg
|
|
};
|
|
video.alert(msgBox), me.close();
|
|
}, this.handleReset = function(irc_cmd) {
|
|
var dlgMsg = "IRC.message.iLOReset", msgBox = {
|
|
messageKey: dlgMsg,
|
|
stayTime: 20,
|
|
title: null,
|
|
icon: null
|
|
};
|
|
video.alert(msgBox), me.close();
|
|
}, this.onhealthchange = function(health_state) {
|
|
this.health_state !== health_state && (this.health_state = health_state, video.health_change(health_state));
|
|
}, this.handleHealthState = function(health_state) {
|
|
if (me.onhealthchange) switch (health_state) {
|
|
case 0:
|
|
me.onhealthchange("OP_STATUS_OK");
|
|
break;
|
|
|
|
case 1:
|
|
me.onhealthchange("OP_STATUS_WARNING");
|
|
break;
|
|
|
|
case 2:
|
|
me.onhealthchange("OP_STATUS_CRITICAL");
|
|
break;
|
|
|
|
default:
|
|
me.onhealthchange("OP_STATUS_UNKNOWN");
|
|
}
|
|
}, this.handlePowerState = function(power_state) {
|
|
if (me.onpowerchange) switch (power_state) {
|
|
case 1:
|
|
me.onpowerchange("ON");
|
|
break;
|
|
|
|
case 0:
|
|
default:
|
|
me.onpowerchange("OFF");
|
|
}
|
|
}, this.onpowerchange = function(power_state) {
|
|
this.power_state !== power_state && (this.power_state = power_state, video.power_change(power_state));
|
|
}, this.cmdsockrecv = function(evt) {
|
|
var irc_cmd = new IRC_CMD(new DataView(evt.data));
|
|
switch (irc_cmd.cmd) {
|
|
case 2:
|
|
video.status_change("Post Complete");
|
|
break;
|
|
|
|
case 3:
|
|
me.handlePowerState(irc_cmd.buf[0]);
|
|
break;
|
|
|
|
case 4:
|
|
me.handleHealthState(irc_cmd.buf[0]);
|
|
break;
|
|
|
|
case 5:
|
|
me.handlePostCode(irc_cmd);
|
|
break;
|
|
|
|
case 6:
|
|
me.handleSeize(irc_cmd);
|
|
break;
|
|
|
|
case 7:
|
|
me.handleAckNotification(irc_cmd);
|
|
break;
|
|
|
|
case 9:
|
|
me.handleShare(irc_cmd);
|
|
break;
|
|
|
|
case 10:
|
|
console.log("IRC: received firmware update notification."), me.handleFirmwareUpdate(irc_cmd);
|
|
break;
|
|
|
|
case 11:
|
|
console.log("IRC: received unauthorized notification."), me.handleUnauthorized(irc_cmd);
|
|
break;
|
|
|
|
case 13:
|
|
break;
|
|
|
|
case 14:
|
|
me.handleLicenseViolation(irc_cmd);
|
|
break;
|
|
|
|
case 15:
|
|
console.log("IRC: iLO reset detected."), me.handleReset(irc_cmd);
|
|
break;
|
|
|
|
case 16:
|
|
case 17:
|
|
break;
|
|
|
|
default:
|
|
console.log("IRC: received unhandled command: ", irc_cmd);
|
|
}
|
|
}, this.open_cmdsocket = function() {
|
|
new Handshake(me.sockaddr, me.sessionKey, 1, 0, function(ws) {
|
|
ws.onmessage = me.cmdsockrecv, ws.onclose = function() {
|
|
me.onclose && me.onclose(), console.log("cmd socket closed");
|
|
}, ws.onerror = function(evt) {
|
|
console.log("cmd socket open error: " + evt);
|
|
}, me.cmdsocket = ws, me.send = me.send_kvm, setTimeout(function() {
|
|
me.all_keys_up();
|
|
}, 2e3);
|
|
}, null);
|
|
}, this.send_kvm = function(arr) {
|
|
this.socket && this.socket.readyState && this.socket.send(new Uint8Array(arr));
|
|
}, this.send_cmd = function(arr) {
|
|
this.cmdsocket && this.cmdsocket.readyState && this.cmdsocket.send(new Uint8Array(arr));
|
|
}, this.send_null = function(arr) {}, this.send = this.send_null, this.sendBool = function(rsp) {
|
|
var cmd = [ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ];
|
|
cmd[0] = rsp ? 4 : 3, this.send_cmd(cmd);
|
|
}, this.kbdsend = function(hidrpt) {
|
|
var cmd;
|
|
cmd = me.version > 2 ? [ 1, 0 ] : [ 255, 214 ], cmd = cmd.concat(hidrpt), me.send(cmd);
|
|
}, this.all_keys_up = function() {
|
|
me.kbdsend([ 0, 0, 0, 0, 0, 0, 0, 0 ]);
|
|
}, this.ctrl_alt_del = function() {
|
|
me.kbdsend([ 5, 0, 76, 0, 0, 0, 0, 0 ]), setTimeout(function() {
|
|
me.kbdsend([ 5, 0, 0, 0, 0, 0, 0, 0 ]), setTimeout(function() {
|
|
me.all_keys_up();
|
|
}, 5);
|
|
}, 250);
|
|
}, this.num_lock = function() {
|
|
me.kbdsend([ 0, 0, 83, 0, 0, 0, 0, 0 ]), setTimeout(function() {
|
|
me.all_keys_up();
|
|
}, 50);
|
|
}, this.caps_lock = function() {
|
|
me.kbdsend([ 0, 0, 57, 0, 0, 0, 0, 0 ]), setTimeout(function() {
|
|
me.all_keys_up();
|
|
}, 50);
|
|
}, this.refresh = function() {
|
|
me.version > 2 ? me.send([ 5, 0 ]) : me.send([ 27, 91, 126 ]);
|
|
}, this.send_ack = function() {
|
|
me.send([ 12, 0 ]);
|
|
}, this.keepalive = function() {
|
|
me.send([ 27, 91, 40 ]);
|
|
}, this.autoalive = function() {
|
|
me.send([ 27, 91, 38 ]);
|
|
}, this.mouse_send = function(rx, ry, ax, ay, button, wheel) {
|
|
if (2 === this.version) return !1;
|
|
var buf = [ 2, 0, 255 & ax, ax >> 8, 255 & ay, ay >> 8, 255 & rx, 255 & ry, button, wheel ];
|
|
return this.send(buf), !0;
|
|
}, this.no_op = function() {
|
|
return !1;
|
|
}, this.close = function() {
|
|
this.socket && this.socket.close(), this.cmdsocket && this.cmdsocket.close();
|
|
}, new Handshake(me.sockaddr, me.sessionKey, KVM_CHANNEL, me.openCommand, function(ws) {
|
|
me.connected = !0, me.socket = ws, me.socket.onmessage = me.sockrecv, me.socket.onclose = function(evt) {
|
|
console.log("WebSocket closed", evt), me.connected = !1, me.onclose && me.onclose();
|
|
}, me.socket.onerror = function(evt) {
|
|
console.log("WebSocket ERROR: ", evt);
|
|
}, me.open_cmdsocket();
|
|
}, function(ws, error) {
|
|
var alertToast = function(key) {
|
|
video.toast({
|
|
textKey: key,
|
|
type: "critical",
|
|
sticky: !0
|
|
});
|
|
};
|
|
switch (error) {
|
|
case DVCNET.CMD_DENIED:
|
|
switch (me.openCommand) {
|
|
case 1:
|
|
alertToast("IRC.message.AcquireDenied");
|
|
break;
|
|
|
|
case 2:
|
|
alertToast("IRC.message.ShareDenied");
|
|
break;
|
|
|
|
case 0:
|
|
default:
|
|
alertToast("IRC.message.SessionLimitReached");
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
var msgKey = "IRC.message.ConsoleInUseNoShare", tmrMsgKey = "IRC.message.TimedAutoDisconnect", msgBox = {
|
|
callback: "send_bool",
|
|
messageKey: msgKey,
|
|
stayTime: 10,
|
|
timerStringKey: tmrMsgKey,
|
|
title: null,
|
|
icon: null,
|
|
buttons: [ {
|
|
textKey: "IRC.control.Acquire",
|
|
isDefault: !0,
|
|
value: !0
|
|
}, {
|
|
textKey: "cancel",
|
|
isDefault: !1,
|
|
value: !1
|
|
} ]
|
|
};
|
|
return video.confirm(msgBox), void me.close();
|
|
|
|
case 3:
|
|
alertToast("status_labels.NOT_SUPPORTED");
|
|
break;
|
|
|
|
case 4:
|
|
video.toast({
|
|
text: "Bad Request",
|
|
type: "critical",
|
|
sticky: !0
|
|
});
|
|
break;
|
|
|
|
case 5:
|
|
video.toast({
|
|
text: "Internal Error",
|
|
type: "critical",
|
|
sticky: !0
|
|
});
|
|
break;
|
|
|
|
case 6:
|
|
case DVCNET.CMD_NO_SESSIONS:
|
|
alertToast("IRC.message.NoFreeSessions");
|
|
break;
|
|
|
|
case 7:
|
|
alertToast("IRC.message.FeatureNotLicensed");
|
|
break;
|
|
|
|
case "SyntaxError":
|
|
alertToast("IRC.message.WebSocketSyntaxError");
|
|
break;
|
|
|
|
case "IE+IPv6":
|
|
alertToast("IRC.message.WebSocketIEIPv6");
|
|
break;
|
|
|
|
default:
|
|
me.ie || isNaN(error) || video.toast({
|
|
text: "Unexpected error: " + error,
|
|
type: "critical",
|
|
sticky: !0
|
|
});
|
|
}
|
|
console.log("Handshake error: ", error), me.close(), me.onclose && me.onclose();
|
|
});
|
|
} |