mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2025-02-02 17:35:57 -05:00
Merge branch 'master' of https://github.com/Ylianst/MeshCentral into master
This commit is contained in:
commit
dea0d96add
@ -35,9 +35,9 @@
|
||||
<Compile Include="agents\modules_meshcmd\amt-wsman.js" />
|
||||
<Compile Include="agents\modules_meshcmd\amt-xml.js" />
|
||||
<Compile Include="agents\modules_meshcmd\amt.js" />
|
||||
<Compile Include="agents\modules_meshcmd\identifiers.js" />
|
||||
<Compile Include="agents\modules_meshcmd\smbios.js" />
|
||||
<Compile Include="agents\modules_meshcmd\sysinfo.js" />
|
||||
<Compile Include="agents\modules_meshcmd\win-securitycenter.js" />
|
||||
<Compile Include="agents\modules_meshcmd_min\amt-ider.min.js" />
|
||||
<Compile Include="agents\modules_meshcmd_min\amt-lme.min.js" />
|
||||
<Compile Include="agents\modules_meshcmd_min\amt-mei.min.js" />
|
||||
@ -63,6 +63,7 @@
|
||||
<Compile Include="agents\modules_meshcore\wifi-scanner.js" />
|
||||
<Compile Include="agents\modules_meshcore\win-console.js" />
|
||||
<Compile Include="agents\modules_meshcore\win-info.js" />
|
||||
<Compile Include="agents\modules_meshcore\win-securitycenter.js" />
|
||||
<Compile Include="agents\modules_meshcore\win-terminal.js" />
|
||||
<Compile Include="agents\modules_meshcore\win-virtual-terminal.js" />
|
||||
<Compile Include="agents\modules_meshcore_min\amt-lme.min.js" />
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -73,7 +73,7 @@
|
||||
"meshId": "Netzkennung",
|
||||
"serverId": "Server-ID",
|
||||
"setup": "Konfiguration",
|
||||
"update": "Aktualisieren",
|
||||
"update": "Updates",
|
||||
"pressok": "Drücken Sie OK, um die Verbindung zu trennen",
|
||||
"elevation": "Für die Installation / Deinstallation des Agenten sind erhöhte Berechtigungen erforderlich.",
|
||||
"sudo": "Bitte versuchen Sie es erneut mit sudo.",
|
||||
|
@ -113,7 +113,7 @@ function run(argv) {
|
||||
//console.log('addedModules = ' + JSON.stringify(addedModules));
|
||||
var actionpath = 'meshaction.txt';
|
||||
if (args.actionfile != null) { actionpath = args.actionfile; }
|
||||
var actions = ['HELP', 'ROUTE', 'MICROLMS', 'AMTCONFIG', 'AMTSCAN', 'AMTPOWER', 'AMTFEATURES', 'AMTNETWORK', 'AMTLOADWEBAPP', 'AMTLOADSMALLWEBAPP', 'AMTLOADLARGEWEBAPP', 'AMTCLEARWEBAPP', 'AMTSTORAGESTATE', 'AMTINFO', 'AMTINFODEBUG', 'AMTVERSIONS', 'AMTHASHES', 'AMTSAVESTATE', 'AMTUUID', 'AMTCCM', 'AMTDEACTIVATE', 'AMTACMDEACTIVATE', 'SMBIOS', 'RAWSMBIOS', 'MESHCOMMANDER', 'AMTAUDITLOG', 'AMTEVENTLOG', 'AMTPRESENCE', 'AMTWIFI', 'AMTWAKE'];
|
||||
var actions = ['HELP', 'ROUTE', 'MICROLMS', 'AMTCONFIG', 'AMTSCAN', 'AMTPOWER', 'AMTFEATURES', 'AMTNETWORK', 'AMTLOADWEBAPP', 'AMTLOADSMALLWEBAPP', 'AMTLOADLARGEWEBAPP', 'AMTCLEARWEBAPP', 'AMTSTORAGESTATE', 'AMTINFO', 'AMTINFODEBUG', 'AMTVERSIONS', 'AMTHASHES', 'AMTSAVESTATE', 'AMTUUID', 'AMTCCM', 'AMTDEACTIVATE', 'AMTACMDEACTIVATE', 'SMBIOS', 'RAWSMBIOS', 'MESHCOMMANDER', 'AMTAUDITLOG', 'AMTEVENTLOG', 'AMTPRESENCE', 'AMTWIFI', 'AMTWAKE', 'AMTSTOPCONFIGURATION'];
|
||||
|
||||
// Load the action file
|
||||
var actionfile = null;
|
||||
@ -428,7 +428,19 @@ function run(argv) {
|
||||
console.log('Proxy set to ' + proxy[0] + ':' + proxyport);
|
||||
}
|
||||
|
||||
if (settings.action == 'smbios') {
|
||||
if (settings.action == 'amtstopconfiguration') {
|
||||
// Stop Intel AMT configuration
|
||||
var amtMeiModule, amtMei;
|
||||
try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { console.log(ex); exit(1); return; }
|
||||
amtMei.on('error', function (e) { console.log('ERROR: ' + e); exit(1); return; });
|
||||
amtMei.stopConfiguration(function (state) {
|
||||
if (state == 3) { console.log("Intel AMT is not in in-provisionning mode."); }
|
||||
else if (state == 1) { console.log("Intel AMT internal error."); }
|
||||
else if (state == 0) { console.log("Success."); }
|
||||
else { console.log("Unknown state: " + state); }
|
||||
exit(1);
|
||||
});
|
||||
} else if (settings.action == 'smbios') {
|
||||
// Display SM BIOS tables in raw form
|
||||
SMBiosTables = require('smbios');
|
||||
SMBiosTables.get(function (data) {
|
||||
@ -1174,9 +1186,28 @@ function configureJsonControl(data) {
|
||||
amtMei.on('error', function (e) { settings.apftunnel.sendMeiDeactivationState(1); });
|
||||
amtMei.unprovision(1, function (status) { settings.apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
|
||||
break;
|
||||
case 'startTlsHostConfig': // Request start of host based TLS ACM activation
|
||||
var amtMeiModule, amtMei;
|
||||
try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { settings.apftunnel.sendStartTlsHostConfigResponse({ state: -103 }); break; }
|
||||
amtMei.on('error', function (e) { settings.apftunnel.sendStartTlsHostConfigResponse({ state: -104 }); });
|
||||
amtMei.startConfigurationHBased(Buffer.from(data.hash, 'hex'), data.hostVpn, data.dnsSuffixList, function (response) {
|
||||
settings.apftunnel.sendStartTlsHostConfigResponse(response);
|
||||
});
|
||||
break;
|
||||
case 'stopConfiguration': // Request Intel AMT stop configuration.
|
||||
var amtMeiModule, amtMei;
|
||||
try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { settings.apftunnel.sendStartTlsHostConfigResponse({ state: -103 }); break; }
|
||||
amtMei.on('error', function (e) { settings.apftunnel.sendStartTlsHostConfigResponse({ state: -104 }); });
|
||||
amtMei.stopConfiguration(function (status) {
|
||||
settings.apftunnel.sendStopConfigurationResponse(status);
|
||||
});
|
||||
break;
|
||||
case 'close': // Close the CIRA-LMS connection
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
console.log("MeshCmd update may be needed, unknown JSON control action: " + data.action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2691,7 +2722,7 @@ function getMeiState(flags, func) {
|
||||
try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { func(null); return; }
|
||||
amtMei.on('error', function (e) { func(null); return; });
|
||||
try {
|
||||
var amtMeiTmpState = { OsHostname: require('os').hostname(), Flags: 0 }; // Flags: 1=EHBC, 2=CCM, 4=ACM
|
||||
var amtMeiTmpState = { 'core-ver': 1, OsHostname: require('os').hostname(), Flags: 0 }; // Flags: 1=EHBC, 2=CCM, 4=ACM
|
||||
amtMei.getProtocolVersion(function (result) { if (result != null) { amtMeiTmpState.MeiVersion = result; } });
|
||||
if ((flags & 1) != 0) { amtMei.getVersion(function (result) { if (result) { amtMeiTmpState.Versions = {}; for (var version in result.Versions) { amtMeiTmpState.Versions[result.Versions[version].Description] = result.Versions[version].Version; } } }); }
|
||||
amtMei.getProvisioningMode(function (result) { if (result) { amtMeiTmpState.ProvisioningMode = result.mode; } });
|
||||
|
@ -1197,6 +1197,18 @@ function handleServerCommand(data) {
|
||||
amtMei.unprovision(1, function (status) { if (apftunnel) apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
|
||||
}
|
||||
if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
|
||||
if (data.action == 'startTlsHostConfig') { // Request start of host based TLS ACM activation
|
||||
var amtMeiModule, amtMei;
|
||||
try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); return; }
|
||||
amtMei.on('error', function (e) { if (apftunnel) apftunnel.sendStartTlsHostConfigResponse({ state: -104 }); });
|
||||
amtMei.startConfigurationHBased(Buffer.from(data.hash, 'hex'), data.hostVpn, data.dnsSuffixList, function (response) { apftunnel.sendStartTlsHostConfigResponse(response); });
|
||||
}
|
||||
if (data.action == 'stopConfiguration') { // Request Intel AMT stop configuration.
|
||||
var amtMeiModule, amtMei;
|
||||
try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); return; }
|
||||
amtMei.on('error', function (e) { if (apftunnel) apftunnel.sendStopConfigurationResponse({ state: -104 }); });
|
||||
amtMei.stopConfiguration(function (status) { apftunnel.sendStopConfigurationResponse(status); });
|
||||
}
|
||||
}
|
||||
apftunnel.onChannelClosed = function () { addAmtEvent('LMS tunnel closed.'); apftunnel = null; }
|
||||
try { apftunnel.connect(); } catch (ex) { }
|
||||
@ -1222,7 +1234,7 @@ function handleServerCommand(data) {
|
||||
break;
|
||||
}
|
||||
case 'coredump':
|
||||
// Set the current agent coredump situation.
|
||||
// Set the current agent coredump situation.s
|
||||
if (data.value === true) {
|
||||
if (process.platform == 'win32') {
|
||||
// TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
|
||||
@ -2746,7 +2758,11 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
|
||||
break;
|
||||
case 'agentupdateex':
|
||||
// Perform an direct agent update without requesting any information from the server, this should not typically be used.
|
||||
agentUpdate_Start(null, { sessionid: sessionid });
|
||||
if (args['_'].length == 1) {
|
||||
if (args['_'][0].startsWith('https://')) { agentUpdate_Start(args['_'][0], { sessionid: sessionid }); } else { response = "Usage: agentupdateex https://server/path"; }
|
||||
} else {
|
||||
agentUpdate_Start(null, { sessionid: sessionid });
|
||||
}
|
||||
break;
|
||||
case 'msh':
|
||||
response = JSON.stringify(_MSH(), null, 2);
|
||||
@ -4134,8 +4150,10 @@ function agentUpdate_Start(updateurl, updateoptions) {
|
||||
var sessionid = (updateoptions != null) ? updateoptions.sessionid : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted
|
||||
|
||||
// If the url starts with *, switch it to use the same protoco, host and port as the control channel.
|
||||
updateurl = getServerTargetUrlEx(updateurl);
|
||||
if (updateurl.startsWith("wss://")) { updateurl = "https://" + updateurl.substring(6); }
|
||||
if (updateurl != null) {
|
||||
updateurl = getServerTargetUrlEx(updateurl);
|
||||
if (updateurl.startsWith("wss://")) { updateurl = "https://" + updateurl.substring(6); }
|
||||
}
|
||||
|
||||
if (agentUpdate_Start._selfupdate != null) {
|
||||
// We were already called, so we will ignore this duplicate request
|
||||
@ -4166,10 +4184,10 @@ function agentUpdate_Start(updateurl, updateoptions) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sessionid != null) { sendConsoleText('Downloading update from: ' + updateurl, sessionid); }
|
||||
if ((sessionid != null) && (updateurl != null)) { sendConsoleText('Downloading update from: ' + updateurl, sessionid); }
|
||||
var options = require('http').parseUri(updateurl != null ? updateurl : require('MeshAgent').ServerUrl);
|
||||
options.protocol = 'https:';
|
||||
if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); }
|
||||
if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); sendConsoleText('Downloading update from: ' + options.path, sessionid); }
|
||||
options.rejectUnauthorized = false;
|
||||
options.checkServerIdentity = function checkServerIdentity(certs) {
|
||||
// If the tunnel certificate matches the control channel certificate, accept the connection
|
||||
|
@ -183,6 +183,8 @@ function CreateAPFClient(parent, args) {
|
||||
|
||||
obj.updateMeiState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'meiState', value: state }); }
|
||||
obj.sendMeiDeactivationState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'deactivate', value: state }); }
|
||||
obj.sendStartTlsHostConfigResponse = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'startTlsHostConfig', value: state }); }
|
||||
obj.sendStopConfigurationResponse = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'stopConfiguration', value: state }); }
|
||||
|
||||
function SendJsonControl(socket, o) {
|
||||
var data = JSON.stringify(o)
|
||||
|
@ -24,22 +24,18 @@ function amt_heci() {
|
||||
|
||||
this._ObjectID = "pthi";
|
||||
this._rq = new Q();
|
||||
this._setupPTHI = function _setupPTHI()
|
||||
{
|
||||
this._setupPTHI = function _setupPTHI() {
|
||||
this._amt = heci.create();
|
||||
this._amt.descriptorMetadata = "amt-pthi";
|
||||
this._amt.BiosVersionLen = 65;
|
||||
this._amt.UnicodeStringLen = 20;
|
||||
|
||||
this._amt.Parent = this;
|
||||
this._amt.on('error', function _amtOnError(e)
|
||||
{
|
||||
if(this.Parent._rq.isEmpty())
|
||||
{
|
||||
this._amt.on('error', function _amtOnError(e) {
|
||||
if (this.Parent._rq.isEmpty()) {
|
||||
this.Parent.emit('error', e); // No pending requests, so propagate the error up
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
// There is a pending request, so fail the pending request
|
||||
var user = this.Parent._rq.deQueue();
|
||||
var params = user.optional;
|
||||
@ -47,17 +43,14 @@ function amt_heci() {
|
||||
params.unshift({ Status: -1 }); // Relay an error
|
||||
callback.apply(this.Parent, params);
|
||||
|
||||
if(!this.Parent._rq.isEmpty())
|
||||
{
|
||||
if (!this.Parent._rq.isEmpty()) {
|
||||
// There are still more pending requests, so try to re-helpconnect MEI
|
||||
this.connect(heci.GUIDS.AMT, { noPipeline: 1 });
|
||||
}
|
||||
}
|
||||
});
|
||||
this._amt.on('connect', function _amtOnConnect()
|
||||
{
|
||||
this.on('data', function _amtOnData(chunk)
|
||||
{
|
||||
this._amt.on('connect', function _amtOnConnect() {
|
||||
this.on('data', function _amtOnData(chunk) {
|
||||
//console.log("Received: " + chunk.length + " bytes");
|
||||
var header = this.Parent.getCommand(chunk);
|
||||
//console.log("CMD = " + header.Command + " (Status: " + header.Status + ") Response = " + header.IsResponse);
|
||||
@ -69,14 +62,12 @@ function amt_heci() {
|
||||
params.unshift(header);
|
||||
callback.apply(this.Parent, params);
|
||||
|
||||
if(this.Parent._rq.isEmpty())
|
||||
{
|
||||
if (this.Parent._rq.isEmpty()) {
|
||||
// No More Requests, we can close PTHI
|
||||
this.Parent._amt.disconnect();
|
||||
this.Parent._amt = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
// Send the next request
|
||||
this.write(this.Parent._rq.peekQueue().send);
|
||||
}
|
||||
@ -93,8 +84,7 @@ function amt_heci() {
|
||||
return (ret);
|
||||
};
|
||||
|
||||
this.sendCommand = function sendCommand()
|
||||
{
|
||||
this.sendCommand = function sendCommand() {
|
||||
if (arguments.length < 3 || typeof (arguments[0]) != 'number' || typeof (arguments[1]) != 'object' || typeof (arguments[2]) != 'function') { throw ('invalid parameters'); }
|
||||
var args = [];
|
||||
for (var i = 3; i < arguments.length; ++i) { args.push(arguments[i]); }
|
||||
@ -102,10 +92,9 @@ function amt_heci() {
|
||||
var header = Buffer.from('010100000000000000000000', 'hex');
|
||||
header.writeUInt32LE(arguments[0] | 0x04000000, 4);
|
||||
header.writeUInt32LE(arguments[1] == null ? 0 : arguments[1].length, 8);
|
||||
this._rq.enQueue({ cmd: arguments[0], func: arguments[2], optional: args , send: (arguments[1] == null ? header : Buffer.concat([header, arguments[1]]))});
|
||||
this._rq.enQueue({ cmd: arguments[0], func: arguments[2], optional: args, send: (arguments[1] == null ? header : Buffer.concat([header, arguments[1]])) });
|
||||
|
||||
if(!this._amt)
|
||||
{
|
||||
if (!this._amt) {
|
||||
this._setupPTHI();
|
||||
this._amt.connect(heci.GUIDS.AMT, { noPipeline: 1 });
|
||||
}
|
||||
@ -117,7 +106,7 @@ function amt_heci() {
|
||||
this.sendCommand(26, null, function (header, fn, opt) {
|
||||
if (header.Status == 0) {
|
||||
var i, CodeVersion = header.Data, val = { BiosVersion: CodeVersion.slice(0, this._amt.BiosVersionLen).toString(), Versions: [] }, v = CodeVersion.slice(this._amt.BiosVersionLen + 4);
|
||||
for (i = 0; i < CodeVersion.readUInt32LE(this._amt.BiosVersionLen) ; ++i) {
|
||||
for (i = 0; i < CodeVersion.readUInt32LE(this._amt.BiosVersionLen); ++i) {
|
||||
val.Versions[i] = { Description: v.slice(2, v.readUInt16LE(0) + 2).toString(), Version: v.slice(4 + this._amt.UnicodeStringLen, 4 + this._amt.UnicodeStringLen + v.readUInt16LE(2 + this._amt.UnicodeStringLen)).toString() };
|
||||
v = v.slice(4 + (2 * this._amt.UnicodeStringLen));
|
||||
}
|
||||
@ -302,34 +291,27 @@ function amt_heci() {
|
||||
this.getLocalSystemAccount = function getLocalSystemAccount(callback) {
|
||||
var optional = [];
|
||||
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
|
||||
this.sendCommand(103, Buffer.alloc(40), function (header, fn, opt)
|
||||
{
|
||||
if (header.Status == 0 && header.Data.length == 68)
|
||||
{
|
||||
this.sendCommand(103, Buffer.alloc(40), function (header, fn, opt) {
|
||||
if (header.Status == 0 && header.Data.length == 68) {
|
||||
opt.unshift({ user: trim(header.Data.slice(0, 33).toString()), pass: trim(header.Data.slice(33, 67).toString()), raw: header.Data });
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
opt.unshift(null);
|
||||
}
|
||||
fn.apply(this, opt);
|
||||
}, callback, optional);
|
||||
}
|
||||
this.getLanInterfaceSettings = function getLanInterfaceSettings(index, callback)
|
||||
{
|
||||
this.getLanInterfaceSettings = function getLanInterfaceSettings(index, callback) {
|
||||
var optional = [];
|
||||
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
|
||||
var ifx = Buffer.alloc(4);
|
||||
ifx.writeUInt32LE(index);
|
||||
this.sendCommand(0x48, ifx, function onGetLanInterfaceSettings(header, fn, opt)
|
||||
{
|
||||
if(header.Status == 0)
|
||||
{
|
||||
this.sendCommand(0x48, ifx, function onGetLanInterfaceSettings(header, fn, opt) {
|
||||
if (header.Status == 0) {
|
||||
var info = {};
|
||||
info.enabled = header.Data.readUInt32LE(0);
|
||||
info.dhcpEnabled = header.Data.readUInt32LE(8);
|
||||
switch(header.Data[12])
|
||||
{
|
||||
switch (header.Data[12]) {
|
||||
case 1:
|
||||
info.dhcpMode = 'ACTIVE'
|
||||
break;
|
||||
@ -341,14 +323,13 @@ function amt_heci() {
|
||||
break;
|
||||
}
|
||||
info.mac = header.Data.slice(14).toString('hex:');
|
||||
|
||||
|
||||
var addr = header.Data.readUInt32LE(4);
|
||||
info.address = ((addr >> 24) & 255) + '.' + ((addr >> 16) & 255) + '.' + ((addr >> 8) & 255) + '.' + (addr & 255);
|
||||
opt.unshift(info);
|
||||
fn.apply(this, opt);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
opt.unshift(null);
|
||||
fn.apply(this, opt);
|
||||
}
|
||||
@ -398,27 +379,81 @@ function amt_heci() {
|
||||
fn.apply(this, opt);
|
||||
}, callback, optional);
|
||||
}
|
||||
this.getProtocolVersion = function getProtocolVersion(callback)
|
||||
{
|
||||
this.getProtocolVersion = function getProtocolVersion(callback) {
|
||||
var optional = [];
|
||||
for (var i = 1; i < arguments.length; ++i) { opt.push(arguments[i]); }
|
||||
|
||||
if (!this._tmpSession) { this._tmpSession = heci.create(); this._tmpSession.parent = this;}
|
||||
this._tmpSession.doIoctl(heci.IOCTL.HECI_VERSION, Buffer.alloc(5), Buffer.alloc(5), function (status, buffer, self, fn, opt)
|
||||
{
|
||||
if (!this._tmpSession) { this._tmpSession = heci.create(); this._tmpSession.parent = this; }
|
||||
this._tmpSession.doIoctl(heci.IOCTL.HECI_VERSION, Buffer.alloc(5), Buffer.alloc(5), function (status, buffer, self, fn, opt) {
|
||||
if (status == 0) {
|
||||
var result = buffer.readUInt8(0).toString() + '.' + buffer.readUInt8(1).toString() + '.' + buffer.readUInt8(2).toString() + '.' + buffer.readUInt16BE(3).toString();
|
||||
opt.unshift(result);
|
||||
fn.apply(self, opt);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
opt.unshift(null);
|
||||
fn.apply(self, opt);
|
||||
}
|
||||
|
||||
}, this, callback, optional);
|
||||
}
|
||||
this.startConfigurationHBased = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
|
||||
if ((certHash == null) || ((certHash.length != 32) && (certHash.length != 48))) { func({ status: -101 }); }
|
||||
this.stopConfiguration(function (status) {
|
||||
if (status == 0) {
|
||||
// We stopped the configuration, wait 20 seconds before starting up again.
|
||||
var f = function tf() { delete tf.parent.xtimeout; tf.parent.startConfigurationHBasedEx(certHash, hostVpn, dnsSuffixList, func); }
|
||||
f.parent = this;
|
||||
this.xtimeout = setTimeout(f, 20000);
|
||||
} else {
|
||||
// We are not in the connect mode, this is good, start configuration right away.
|
||||
this.startConfigurationHBasedEx(certHash, hostVpn, dnsSuffixList, func);
|
||||
}
|
||||
})
|
||||
}
|
||||
this.startConfigurationHBasedEx = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
|
||||
var optional = [];
|
||||
for (var i = 4; i < arguments.length; ++i) { optional.push(arguments[i]); }
|
||||
|
||||
// Format the command
|
||||
var data = Buffer.alloc(4 + 64 + 4 + 4 + 320);
|
||||
data.writeUInt32LE((certHash.length == 48) ? 3 : 2, 0); // Write certificate hash type: SHA256 = 2, SHA384 = 3
|
||||
certHash.copy(data, 4); // Write the hash
|
||||
data.writeUInt32LE(hostVpn ? 1 : 0, 68); // Write is HostVPN is enabled
|
||||
if (dnsSuffixList != null) {
|
||||
data.writeUInt32LE(dnsSuffixList.length, 72); // Write the number of DNS Suffix, from 0 to 4
|
||||
var ptr = 76;
|
||||
for (var i = 0; i < dnsSuffixList.length; i++) { ptr += data.write(dnsSuffixList[i], ptr) + 1; } // Write up to 4 DNS Suffix with null seperation.
|
||||
}
|
||||
|
||||
// Send the command
|
||||
this.sendCommand(139, data, function (header, fn, opt) {
|
||||
if (header.Status == 0) {
|
||||
var amtHash = null;
|
||||
if (header.Data[0] == 2) { amtHash = header.Data.slice(1, 33); } // SHA256
|
||||
if (header.Data[0] == 3) { amtHash = header.Data.slice(1, 49); } // SHA384
|
||||
opt.unshift({ status: header.Status, hash: amtHash.toString('hex') });
|
||||
} else {
|
||||
opt.unshift({ status: header.Status });
|
||||
}
|
||||
fn.apply(this, opt);
|
||||
}, func, optional);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = amt_heci;
|
||||
module.exports = amt_heci;
|
||||
|
||||
|
||||
/*
|
||||
AMT_STATUS_SUCCESS = 0,
|
||||
AMT_STATUS_INTERNAL_ERROR = 1,
|
||||
AMT_STATUS_INVALID_AMT_MODE = 3,
|
||||
AMT_STATUS_INVALID_MESSAGE_LENGTH = 4,
|
||||
AMT_STATUS_MAX_LIMIT_REACHED = 23,
|
||||
AMT_STATUS_INVALID_PARAMETER = 36,
|
||||
AMT_STATUS_RNG_GENERATION_IN_PROGRESS = 47,
|
||||
AMT_STATUS_RNG_NOT_READY = 48,
|
||||
AMT_STATUS_CERTIFICATE_NOT_READY = 49,
|
||||
AMT_STATUS_INVALID_HANDLE = 2053
|
||||
AMT_STATUS_NOT_FOUND = 2068,
|
||||
*/
|
@ -183,6 +183,8 @@ function CreateAPFClient(parent, args) {
|
||||
|
||||
obj.updateMeiState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'meiState', value: state }); }
|
||||
obj.sendMeiDeactivationState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'deactivate', value: state }); }
|
||||
obj.sendStartTlsHostConfigResponse = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'startTlsHostConfig', value: state }); }
|
||||
obj.sendStopConfigurationResponse = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'stopConfiguration', value: state }); }
|
||||
|
||||
function SendJsonControl(socket, o) {
|
||||
var data = JSON.stringify(o)
|
||||
|
@ -87,7 +87,7 @@ function AmtManager(agent, db, isdebug) {
|
||||
obj.getMeiState = function(flags, func) {
|
||||
if ((amtMei == null) || (amtMeiState < 2)) { if (func != null) { func(null); } return; }
|
||||
try {
|
||||
var amtMeiTmpState = { OsHostname: require('os').hostname(), Flags: 0 }; // Flags: 1=EHBC, 2=CCM, 4=ACM
|
||||
var amtMeiTmpState = { 'core-ver': 1, OsHostname: require('os').hostname(), Flags: 0 }; // Flags: 1=EHBC, 2=CCM, 4=ACM
|
||||
if (getMeiStateCache.MeiVersion != null) { amtMeiTmpState.MeiVersion = getMeiStateCache.MeiVersion; } else { amtMei.getProtocolVersion(function (result) { if (result != null) { getMeiStateCache.MeiVersion = amtMeiTmpState.MeiVersion = result; } }); }
|
||||
if ((flags & 1) != 0) {
|
||||
if (getMeiStateCache.Versions != null) {
|
||||
@ -157,6 +157,12 @@ function AmtManager(agent, db, isdebug) {
|
||||
}
|
||||
}
|
||||
|
||||
// Start host based ACM activation with TLS
|
||||
obj.startConfigurationHBased = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
|
||||
if ((amtMei == null) || (amtMeiState < 2)) { if (func != null) { func({ status: -100 }); } return; }
|
||||
amtMei.startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = AmtManager;
|
||||
|
@ -24,22 +24,18 @@ function amt_heci() {
|
||||
|
||||
this._ObjectID = "pthi";
|
||||
this._rq = new Q();
|
||||
this._setupPTHI = function _setupPTHI()
|
||||
{
|
||||
this._setupPTHI = function _setupPTHI() {
|
||||
this._amt = heci.create();
|
||||
this._amt.descriptorMetadata = "amt-pthi";
|
||||
this._amt.BiosVersionLen = 65;
|
||||
this._amt.UnicodeStringLen = 20;
|
||||
|
||||
this._amt.Parent = this;
|
||||
this._amt.on('error', function _amtOnError(e)
|
||||
{
|
||||
if(this.Parent._rq.isEmpty())
|
||||
{
|
||||
this._amt.on('error', function _amtOnError(e) {
|
||||
if (this.Parent._rq.isEmpty()) {
|
||||
this.Parent.emit('error', e); // No pending requests, so propagate the error up
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
// There is a pending request, so fail the pending request
|
||||
var user = this.Parent._rq.deQueue();
|
||||
var params = user.optional;
|
||||
@ -47,17 +43,14 @@ function amt_heci() {
|
||||
params.unshift({ Status: -1 }); // Relay an error
|
||||
callback.apply(this.Parent, params);
|
||||
|
||||
if(!this.Parent._rq.isEmpty())
|
||||
{
|
||||
if (!this.Parent._rq.isEmpty()) {
|
||||
// There are still more pending requests, so try to re-helpconnect MEI
|
||||
this.connect(heci.GUIDS.AMT, { noPipeline: 1 });
|
||||
}
|
||||
}
|
||||
});
|
||||
this._amt.on('connect', function _amtOnConnect()
|
||||
{
|
||||
this.on('data', function _amtOnData(chunk)
|
||||
{
|
||||
this._amt.on('connect', function _amtOnConnect() {
|
||||
this.on('data', function _amtOnData(chunk) {
|
||||
//console.log("Received: " + chunk.length + " bytes");
|
||||
var header = this.Parent.getCommand(chunk);
|
||||
//console.log("CMD = " + header.Command + " (Status: " + header.Status + ") Response = " + header.IsResponse);
|
||||
@ -69,14 +62,12 @@ function amt_heci() {
|
||||
params.unshift(header);
|
||||
callback.apply(this.Parent, params);
|
||||
|
||||
if(this.Parent._rq.isEmpty())
|
||||
{
|
||||
if (this.Parent._rq.isEmpty()) {
|
||||
// No More Requests, we can close PTHI
|
||||
this.Parent._amt.disconnect();
|
||||
this.Parent._amt = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
// Send the next request
|
||||
this.write(this.Parent._rq.peekQueue().send);
|
||||
}
|
||||
@ -93,8 +84,7 @@ function amt_heci() {
|
||||
return (ret);
|
||||
};
|
||||
|
||||
this.sendCommand = function sendCommand()
|
||||
{
|
||||
this.sendCommand = function sendCommand() {
|
||||
if (arguments.length < 3 || typeof (arguments[0]) != 'number' || typeof (arguments[1]) != 'object' || typeof (arguments[2]) != 'function') { throw ('invalid parameters'); }
|
||||
var args = [];
|
||||
for (var i = 3; i < arguments.length; ++i) { args.push(arguments[i]); }
|
||||
@ -102,10 +92,9 @@ function amt_heci() {
|
||||
var header = Buffer.from('010100000000000000000000', 'hex');
|
||||
header.writeUInt32LE(arguments[0] | 0x04000000, 4);
|
||||
header.writeUInt32LE(arguments[1] == null ? 0 : arguments[1].length, 8);
|
||||
this._rq.enQueue({ cmd: arguments[0], func: arguments[2], optional: args , send: (arguments[1] == null ? header : Buffer.concat([header, arguments[1]]))});
|
||||
this._rq.enQueue({ cmd: arguments[0], func: arguments[2], optional: args, send: (arguments[1] == null ? header : Buffer.concat([header, arguments[1]])) });
|
||||
|
||||
if(!this._amt)
|
||||
{
|
||||
if (!this._amt) {
|
||||
this._setupPTHI();
|
||||
this._amt.connect(heci.GUIDS.AMT, { noPipeline: 1 });
|
||||
}
|
||||
@ -117,7 +106,7 @@ function amt_heci() {
|
||||
this.sendCommand(26, null, function (header, fn, opt) {
|
||||
if (header.Status == 0) {
|
||||
var i, CodeVersion = header.Data, val = { BiosVersion: CodeVersion.slice(0, this._amt.BiosVersionLen).toString(), Versions: [] }, v = CodeVersion.slice(this._amt.BiosVersionLen + 4);
|
||||
for (i = 0; i < CodeVersion.readUInt32LE(this._amt.BiosVersionLen) ; ++i) {
|
||||
for (i = 0; i < CodeVersion.readUInt32LE(this._amt.BiosVersionLen); ++i) {
|
||||
val.Versions[i] = { Description: v.slice(2, v.readUInt16LE(0) + 2).toString(), Version: v.slice(4 + this._amt.UnicodeStringLen, 4 + this._amt.UnicodeStringLen + v.readUInt16LE(2 + this._amt.UnicodeStringLen)).toString() };
|
||||
v = v.slice(4 + (2 * this._amt.UnicodeStringLen));
|
||||
}
|
||||
@ -302,34 +291,27 @@ function amt_heci() {
|
||||
this.getLocalSystemAccount = function getLocalSystemAccount(callback) {
|
||||
var optional = [];
|
||||
for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
|
||||
this.sendCommand(103, Buffer.alloc(40), function (header, fn, opt)
|
||||
{
|
||||
if (header.Status == 0 && header.Data.length == 68)
|
||||
{
|
||||
this.sendCommand(103, Buffer.alloc(40), function (header, fn, opt) {
|
||||
if (header.Status == 0 && header.Data.length == 68) {
|
||||
opt.unshift({ user: trim(header.Data.slice(0, 33).toString()), pass: trim(header.Data.slice(33, 67).toString()), raw: header.Data });
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
opt.unshift(null);
|
||||
}
|
||||
fn.apply(this, opt);
|
||||
}, callback, optional);
|
||||
}
|
||||
this.getLanInterfaceSettings = function getLanInterfaceSettings(index, callback)
|
||||
{
|
||||
this.getLanInterfaceSettings = function getLanInterfaceSettings(index, callback) {
|
||||
var optional = [];
|
||||
for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
|
||||
var ifx = Buffer.alloc(4);
|
||||
ifx.writeUInt32LE(index);
|
||||
this.sendCommand(0x48, ifx, function onGetLanInterfaceSettings(header, fn, opt)
|
||||
{
|
||||
if(header.Status == 0)
|
||||
{
|
||||
this.sendCommand(0x48, ifx, function onGetLanInterfaceSettings(header, fn, opt) {
|
||||
if (header.Status == 0) {
|
||||
var info = {};
|
||||
info.enabled = header.Data.readUInt32LE(0);
|
||||
info.dhcpEnabled = header.Data.readUInt32LE(8);
|
||||
switch(header.Data[12])
|
||||
{
|
||||
switch (header.Data[12]) {
|
||||
case 1:
|
||||
info.dhcpMode = 'ACTIVE'
|
||||
break;
|
||||
@ -341,14 +323,13 @@ function amt_heci() {
|
||||
break;
|
||||
}
|
||||
info.mac = header.Data.slice(14).toString('hex:');
|
||||
|
||||
|
||||
var addr = header.Data.readUInt32LE(4);
|
||||
info.address = ((addr >> 24) & 255) + '.' + ((addr >> 16) & 255) + '.' + ((addr >> 8) & 255) + '.' + (addr & 255);
|
||||
opt.unshift(info);
|
||||
fn.apply(this, opt);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
opt.unshift(null);
|
||||
fn.apply(this, opt);
|
||||
}
|
||||
@ -398,27 +379,81 @@ function amt_heci() {
|
||||
fn.apply(this, opt);
|
||||
}, callback, optional);
|
||||
}
|
||||
this.getProtocolVersion = function getProtocolVersion(callback)
|
||||
{
|
||||
this.getProtocolVersion = function getProtocolVersion(callback) {
|
||||
var optional = [];
|
||||
for (var i = 1; i < arguments.length; ++i) { opt.push(arguments[i]); }
|
||||
|
||||
if (!this._tmpSession) { this._tmpSession = heci.create(); this._tmpSession.parent = this;}
|
||||
this._tmpSession.doIoctl(heci.IOCTL.HECI_VERSION, Buffer.alloc(5), Buffer.alloc(5), function (status, buffer, self, fn, opt)
|
||||
{
|
||||
if (!this._tmpSession) { this._tmpSession = heci.create(); this._tmpSession.parent = this; }
|
||||
this._tmpSession.doIoctl(heci.IOCTL.HECI_VERSION, Buffer.alloc(5), Buffer.alloc(5), function (status, buffer, self, fn, opt) {
|
||||
if (status == 0) {
|
||||
var result = buffer.readUInt8(0).toString() + '.' + buffer.readUInt8(1).toString() + '.' + buffer.readUInt8(2).toString() + '.' + buffer.readUInt16BE(3).toString();
|
||||
opt.unshift(result);
|
||||
fn.apply(self, opt);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
opt.unshift(null);
|
||||
fn.apply(self, opt);
|
||||
}
|
||||
|
||||
}, this, callback, optional);
|
||||
}
|
||||
this.startConfigurationHBased = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
|
||||
if ((certHash == null) || ((certHash.length != 32) && (certHash.length != 48))) { func({ status: -101 }); }
|
||||
this.stopConfiguration(function (status) {
|
||||
if (status == 0) {
|
||||
// We stopped the configuration, wait 20 seconds before starting up again.
|
||||
var f = function tf() { delete tf.parent.xtimeout; tf.parent.startConfigurationHBasedEx(certHash, hostVpn, dnsSuffixList, func); }
|
||||
f.parent = this;
|
||||
this.xtimeout = setTimeout(f, 20000);
|
||||
} else {
|
||||
// We are not in the connect mode, this is good, start configuration right away.
|
||||
this.startConfigurationHBasedEx(certHash, hostVpn, dnsSuffixList, func);
|
||||
}
|
||||
})
|
||||
}
|
||||
this.startConfigurationHBasedEx = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
|
||||
var optional = [];
|
||||
for (var i = 4; i < arguments.length; ++i) { optional.push(arguments[i]); }
|
||||
|
||||
// Format the command
|
||||
var data = Buffer.alloc(4 + 64 + 4 + 4 + 320);
|
||||
data.writeUInt32LE((certHash.length == 48) ? 3 : 2, 0); // Write certificate hash type: SHA256 = 2, SHA384 = 3
|
||||
certHash.copy(data, 4); // Write the hash
|
||||
data.writeUInt32LE(hostVpn ? 1 : 0, 68); // Write is HostVPN is enabled
|
||||
if (dnsSuffixList != null) {
|
||||
data.writeUInt32LE(dnsSuffixList.length, 72); // Write the number of DNS Suffix, from 0 to 4
|
||||
var ptr = 76;
|
||||
for (var i = 0; i < dnsSuffixList.length; i++) { ptr += data.write(dnsSuffixList[i], ptr) + 1; } // Write up to 4 DNS Suffix with null seperation.
|
||||
}
|
||||
|
||||
// Send the command
|
||||
this.sendCommand(139, data, function (header, fn, opt) {
|
||||
if (header.Status == 0) {
|
||||
var amtHash = null;
|
||||
if (header.Data[0] == 2) { amtHash = header.Data.slice(1, 33); } // SHA256
|
||||
if (header.Data[0] == 3) { amtHash = header.Data.slice(1, 49); } // SHA384
|
||||
opt.unshift({ status: header.Status, hash: amtHash.toString('hex') });
|
||||
} else {
|
||||
opt.unshift({ status: header.Status });
|
||||
}
|
||||
fn.apply(this, opt);
|
||||
}, func, optional);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = amt_heci;
|
||||
module.exports = amt_heci;
|
||||
|
||||
|
||||
/*
|
||||
AMT_STATUS_SUCCESS = 0,
|
||||
AMT_STATUS_INTERNAL_ERROR = 1,
|
||||
AMT_STATUS_INVALID_AMT_MODE = 3,
|
||||
AMT_STATUS_INVALID_MESSAGE_LENGTH = 4,
|
||||
AMT_STATUS_MAX_LIMIT_REACHED = 23,
|
||||
AMT_STATUS_INVALID_PARAMETER = 36,
|
||||
AMT_STATUS_RNG_GENERATION_IN_PROGRESS = 47,
|
||||
AMT_STATUS_RNG_NOT_READY = 48,
|
||||
AMT_STATUS_CERTIFICATE_NOT_READY = 49,
|
||||
AMT_STATUS_INVALID_HANDLE = 2053
|
||||
AMT_STATUS_NOT_FOUND = 2068,
|
||||
*/
|
@ -358,8 +358,10 @@ function agentUpdate_Start(updateurl, updateoptions) {
|
||||
var sessionid = (updateoptions != null) ? updateoptions.sessionid : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted
|
||||
|
||||
// If the url starts with *, switch it to use the same protoco, host and port as the control channel.
|
||||
updateurl = getServerTargetUrlEx(updateurl);
|
||||
if (updateurl.startsWith("wss://")) { updateurl = "https://" + updateurl.substring(6); }
|
||||
if (updateurl != null) {
|
||||
updateurl = getServerTargetUrlEx(updateurl);
|
||||
if (updateurl.startsWith("wss://")) { updateurl = "https://" + updateurl.substring(6); }
|
||||
}
|
||||
|
||||
if (agentUpdate_Start._selfupdate != null) {
|
||||
// We were already called, so we will ignore this duplicate request
|
||||
@ -390,10 +392,10 @@ function agentUpdate_Start(updateurl, updateoptions) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sessionid != null) { sendConsoleText('Downloading update from: ' + updateurl, sessionid); }
|
||||
if ((sessionid != null) && (updateurl != null)) { sendConsoleText('Downloading update from: ' + updateurl, sessionid); }
|
||||
var options = require('http').parseUri(updateurl != null ? updateurl : require('MeshAgent').ServerUrl);
|
||||
options.protocol = 'https:';
|
||||
if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); }
|
||||
if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); sendConsoleText('Downloading update from: ' + options.path, sessionid); }
|
||||
options.rejectUnauthorized = false;
|
||||
options.checkServerIdentity = function checkServerIdentity(certs) {
|
||||
// If the tunnel certificate matches the control channel certificate, accept the connection
|
||||
@ -1007,7 +1009,7 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
|
||||
var response = null;
|
||||
switch (cmd) {
|
||||
case 'help':
|
||||
response = "Available commands are: agentupdate, dbkeys, dbget, dbset, dbcompact, eval, netinfo, osinfo, setdebug, versions.";
|
||||
response = "Available commands are: agentupdate, agentupdateex, dbkeys, dbget, dbset, dbcompact, eval, netinfo, osinfo, setdebug, versions.";
|
||||
break;
|
||||
case '_descriptors':
|
||||
response = 'Open Descriptors: ' + JSON.stringify(getOpenDescriptors());
|
||||
@ -1021,7 +1023,11 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
|
||||
break;
|
||||
case 'agentupdateex':
|
||||
// Perform an direct agent update without requesting any information from the server, this should not typically be used.
|
||||
agentUpdate_Start(null, { sessionid: sessionid });
|
||||
if (args['_'].length == 1) {
|
||||
if (args['_'][0].startsWith('https://')) { agentUpdate_Start(args['_'][0], { sessionid: sessionid }); } else { response = "Usage: agentupdateex https://server/path"; }
|
||||
} else {
|
||||
agentUpdate_Start(null, { sessionid: sessionid });
|
||||
}
|
||||
break;
|
||||
case 'eval':
|
||||
{ // Eval JavaScript
|
||||
|
@ -242,9 +242,9 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, mpsConn
|
||||
var options = { socket: ser, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false };
|
||||
if (obj.xtlsMethod == 1) { options.secureProtocol = 'TLSv1_method'; }
|
||||
if (obj.xtlsoptions) {
|
||||
if (obj.xtlsoptions.ca) options.ca = obj.xtlsoptions.ca;
|
||||
if (obj.xtlsoptions.cert) options.cert = obj.xtlsoptions.cert;
|
||||
if (obj.xtlsoptions.key) options.key = obj.xtlsoptions.key;
|
||||
if (obj.xtlsoptions.ca) { options.ca = obj.xtlsoptions.ca; }
|
||||
if (obj.xtlsoptions.cert) { options.cert = obj.xtlsoptions.cert; }
|
||||
if (obj.xtlsoptions.key) { options.key = obj.xtlsoptions.key; }
|
||||
}
|
||||
|
||||
obj.socket = obj.tls.connect(obj.port, obj.host, options, obj.xxOnSocketConnected);
|
||||
|
242
amtmanager.js
242
amtmanager.js
@ -114,8 +114,8 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
}
|
||||
|
||||
// Remove an Intel AMT managed device
|
||||
function removeAmtDevice(dev) {
|
||||
parent.debug('amt', dev.name, "Remove device", dev.nodeid, dev.connType);
|
||||
function removeAmtDevice(dev, tag) {
|
||||
parent.debug('amt', dev.name, "Remove device", dev.nodeid, dev.connType, tag);
|
||||
|
||||
// Find the device in the list
|
||||
var devices = obj.amtDevices[dev.nodeid];
|
||||
@ -202,7 +202,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
if (devices != null) { for (var i in devices) { if ((devices[i].mpsConnection == connection) || (devices[i].host == connection)) { dev = devices[i]; } } }
|
||||
if (dev == null) return false; // We are not managing this device on this connection
|
||||
parent.debug('amt', dev.name, "Stop Management", nodeid, connType);
|
||||
return removeAmtDevice(dev);
|
||||
return removeAmtDevice(dev, 1);
|
||||
}
|
||||
|
||||
// Get a string status of the managed devices
|
||||
@ -242,9 +242,37 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
deactivateIntelAmtCCMEx(dev, jsondata.value);
|
||||
break;
|
||||
case 'meiState':
|
||||
if (dev.pendingUpdatedMeiState != 1) break;
|
||||
delete dev.pendingUpdatedMeiState;
|
||||
attemptInitialContact(dev);
|
||||
if (dev.acmactivate == 1) {
|
||||
// Continue ACM activation
|
||||
dev.consoleMsg("Got new Intel AMT MEI state. Holding 40 seconds prior to ACM activation...");
|
||||
delete dev.acmactivate;
|
||||
var continueAcmFunc = function continueAcm() { if (isAmtDeviceValid(continueAcm.dev)) { activateIntelAmtAcmEx0(continueAcm.dev); } }
|
||||
continueAcmFunc.dev = dev;
|
||||
setTimeout(continueAcmFunc, 40000);
|
||||
} else {
|
||||
if (dev.pendingUpdatedMeiState != 1) break;
|
||||
delete dev.pendingUpdatedMeiState;
|
||||
attemptInitialContact(dev);
|
||||
}
|
||||
break;
|
||||
case 'startTlsHostConfig':
|
||||
if (dev.acmTlsInfo == null) break;
|
||||
if ((typeof jsondata.value != 'object') || (typeof jsondata.value.status != 'number') || (jsondata.value.status != 0)) {
|
||||
removeAmtDevice(dev, 2); // Failed to start TLS configuration
|
||||
} else {
|
||||
activateIntelAmtTlsAcmEx(dev, jsondata.value); // Start TLS activation.
|
||||
}
|
||||
break;
|
||||
case 'stopConfiguration':
|
||||
if (dev.acmactivate != 1) break;
|
||||
if (jsondata.value == 3) { delete dev.acmactivate; activateIntelAmtAcmEx0(dev); } // Intel AMT was already not in in-provisioning state, keep going right away.
|
||||
else if (jsondata.value == 0) {
|
||||
dev.consoleMsg("Cleared in-provisioning state. Holding 30 seconds prior to getting Intel AMT MEI state...");
|
||||
var askStateFunc = function askState() { if (isAmtDeviceValid(askState.dev)) { askState.dev.controlMsg({ action: 'mestate' }); } }
|
||||
askStateFunc.dev = dev;
|
||||
setTimeout(askStateFunc, 30000);
|
||||
}
|
||||
else { dev.consoleMsg("Unknown stopConfiguration() state of " + jsondata.value + ". Continuing with ACM activation..."); delete dev.acmactivate; activateIntelAmtAcmEx0(dev); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -272,10 +300,23 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
for (var i in devices) {
|
||||
var dev = devices[i];
|
||||
dev.name = event.node.name;
|
||||
if (event.node.intelamt != null) { dev.intelamt = event.node.intelamt; }
|
||||
|
||||
// If there are any changes, apply them.
|
||||
if (event.node.intelamt != null) {
|
||||
if ((typeof event.node.intelamt.version == 'string') && (event.node.intelamt.version != dev.intelamt.ver)) { dev.intelamt.ver = event.node.intelamt.version; }
|
||||
if ((typeof event.node.intelamt.user == 'string') && (event.node.intelamt.user != dev.intelamt.user)) { dev.intelamt.user = event.node.intelamt.user; }
|
||||
if ((typeof event.node.intelamt.pass == 'string') && (event.node.intelamt.pass != dev.intelamt.pass)) { dev.intelamt.pass = event.node.intelamt.pass; }
|
||||
if ((typeof event.node.intelamt.mpspass == 'string') && (event.node.intelamt.mpspass != dev.intelamt.mpspass)) { dev.intelamt.mpspass = event.node.intelamt.mpspass; }
|
||||
if ((typeof event.node.intelamt.host == 'string') && (event.node.intelamt.host != dev.intelamt.host)) { dev.intelamt.host = event.node.intelamt.host; }
|
||||
if ((typeof event.node.intelamt.realm == 'string') && (event.node.intelamt.realm != dev.intelamt.realm)) { dev.intelamt.realm = event.node.intelamt.realm; }
|
||||
if ((typeof event.node.intelamt.hash == 'string') && (event.node.intelamt.hash != dev.intelamt.hash)) { dev.intelamt.hash = event.node.intelamt.hash; }
|
||||
if ((typeof event.node.intelamt.tls == 'number') && (event.node.intelamt.tls != dev.intelamt.tls)) { dev.intelamt.tls = event.node.intelamt.tls; }
|
||||
if ((typeof event.node.intelamt.state == 'number') && (event.node.intelamt.state != dev.intelamt.state)) { dev.intelamt.state = event.node.intelamt.state; }
|
||||
}
|
||||
|
||||
if ((dev.connType == 3) && (dev.host != event.node.host)) {
|
||||
dev.host = event.node.host; // The host has changed, if we are connected to this device locally, we need to reset.
|
||||
removeAmtDevice(dev); // We are going to wait for the AMT scanned to find this device again.
|
||||
removeAmtDevice(dev, 3); // We are going to wait for the AMT scanned to find this device again.
|
||||
rescan = true;
|
||||
}
|
||||
}
|
||||
@ -303,11 +344,11 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
// Update information about a device
|
||||
function fetchIntelAmtInformation(dev) {
|
||||
parent.db.Get(dev.nodeid, function (err, nodes) {
|
||||
if ((nodes == null) || (nodes.length != 1)) { removeAmtDevice(dev); return; }
|
||||
if ((nodes == null) || (nodes.length != 1)) { removeAmtDevice(dev, 4); return; }
|
||||
const node = nodes[0];
|
||||
if ((node.intelamt == null) || (node.meshid == null)) { removeAmtDevice(dev); return; }
|
||||
if ((node.intelamt == null) || (node.meshid == null)) { removeAmtDevice(dev, 5); return; }
|
||||
const mesh = parent.webserver.meshes[node.meshid];
|
||||
if (mesh == null) { removeAmtDevice(dev); return; }
|
||||
if (mesh == null) { removeAmtDevice(dev, 6); return; }
|
||||
if (dev == null) { return; }
|
||||
|
||||
// Fetch Intel AMT setup policy
|
||||
@ -345,6 +386,9 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
if (typeof dev.mpsConnection.tag.meiState['ProvisioningState'] == 'number') {
|
||||
dev.intelamt.state = dev.aquired.state = dev.mpsConnection.tag.meiState['ProvisioningState'];
|
||||
}
|
||||
if ((typeof dev.mpsConnection.tag.meiState['Versions'] == 'object') && (typeof dev.mpsConnection.tag.meiState['Versions']['AMT'] == 'string')) {
|
||||
dev.intelamt.ver = dev.aquired.version = dev.mpsConnection.tag.meiState['Versions']['AMT'];
|
||||
}
|
||||
if (typeof dev.mpsConnection.tag.meiState['Flags'] == 'number') {
|
||||
const flags = dev.intelamt.flags = dev.mpsConnection.tag.meiState['Flags'];
|
||||
if (flags & 2) { dev.aquired.controlMode = 1; } // CCM
|
||||
@ -354,7 +398,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
}
|
||||
|
||||
// If there is no Intel AMT policy for this device, stop here.
|
||||
//if (amtPolicy == 0) { dev.consoleMsg("Done."); removeAmtDevice(dev); return; }
|
||||
//if (amtPolicy == 0) { dev.consoleMsg("Done."); removeAmtDevice(dev, 7); return; }
|
||||
|
||||
// Initiate the communication to Intel AMT
|
||||
dev.consoleMsg("Checking Intel AMT state...");
|
||||
@ -392,7 +436,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
|
||||
// See if we need to try different credentials
|
||||
if ((dev.acctry == null) && ((typeof dev.intelamt.user != 'string') || (typeof dev.intelamt.pass != 'string'))) {
|
||||
if ((obj.amtAdminAccounts[dev.domainid] != null) && (obj.amtAdminAccounts[dev.domainid].length > 0)) { dev.acctry = 0; } else { removeAmtDevice(dev); return; }
|
||||
if ((obj.amtAdminAccounts[dev.domainid] != null) && (obj.amtAdminAccounts[dev.domainid].length > 0)) { dev.acctry = 0; } else { removeAmtDevice(dev, 8); return; }
|
||||
}
|
||||
|
||||
switch (dev.connType) {
|
||||
@ -402,7 +446,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
|
||||
// Check to see if CIRA is connected on this server.
|
||||
var ciraconn = dev.mpsConnection;
|
||||
if ((ciraconn == null) || (ciraconn.tag == null) || (ciraconn.tag.boundPorts == null)) { removeAmtDevice(dev); return; } // CIRA connection is not on this server, no need to deal with this device anymore.
|
||||
if ((ciraconn == null) || (ciraconn.tag == null) || (ciraconn.tag.boundPorts == null)) { removeAmtDevice(dev, 9); return; } // CIRA connection is not on this server, no need to deal with this device anymore.
|
||||
|
||||
// See what user/pass to try.
|
||||
var user = null, pass = null;
|
||||
@ -414,7 +458,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
var dotls = -1;
|
||||
if (ciraconn.tag.boundPorts.indexOf('16992')) { dotls = 0; }
|
||||
else if (ciraconn.tag.boundPorts.indexOf('16993')) { dotls = 1; }
|
||||
if (dotls == -1) { removeAmtDevice(dev); return; } // The Intel AMT ports are not open, not a device we can deal with.
|
||||
if (dotls == -1) { removeAmtDevice(dev, 10); return; } // The Intel AMT ports are not open, not a device we can deal with.
|
||||
|
||||
// Connect now
|
||||
parent.debug('amt', dev.name, 'CIRA-Connect', (dotls == 1) ? "TLS" : "NoTLS", user, pass);
|
||||
@ -435,7 +479,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
// Handle the case where the Intel AMT relay or LMS is connected (connType 1 or 2)
|
||||
// Check to see if CIRA is connected on this server.
|
||||
var ciraconn = dev.mpsConnection;
|
||||
if ((ciraconn == null) || (ciraconn.tag == null) || (ciraconn.tag.boundPorts == null)) { removeAmtDevice(dev); return; } // Relay connection not valid
|
||||
if ((ciraconn == null) || (ciraconn.tag == null) || (ciraconn.tag.boundPorts == null)) { console.log('r2'); removeAmtDevice(dev, 11); return; } // Relay connection not valid
|
||||
|
||||
// See what user/pass to try.
|
||||
var user = null, pass = null;
|
||||
@ -459,9 +503,12 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], attemptLocalConnectResponse);
|
||||
break;
|
||||
case 3: // Local LAN
|
||||
// Check if Intel AMT is activated. If not, stop here.
|
||||
if ((dev.intelamt == null) || ((dev.intelamt.state != null) && (dev.intelamt.state != 2))) { removeAmtDevice(dev, 12); return; }
|
||||
|
||||
// Handle the case where the Intel AMT local scanner found the device (connType 3)
|
||||
parent.debug('amt', dev.name, "Attempt Initial Local Contact", dev.connType, dev.host);
|
||||
if (typeof dev.host != 'string') { removeAmtDevice(dev); return; } // Local connection not valid
|
||||
if (typeof dev.host != 'string') { removeAmtDevice(dev, 13); return; } // Local connection not valid
|
||||
|
||||
// Since we don't allow two or more connections to the same host, check if a pending connection is active.
|
||||
if (obj.activeLocalConnections[dev.host] != null) {
|
||||
@ -592,7 +639,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
UpdateDevice(dev);
|
||||
}
|
||||
//console.log(dev.nodeid, dev.name, dev.host, status, 'Bad response');
|
||||
removeAmtDevice(dev);
|
||||
removeAmtDevice(dev, 14);
|
||||
}
|
||||
}
|
||||
|
||||
@ -605,7 +652,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
function UpdateDevice(dev) {
|
||||
// Check that the mesh exists
|
||||
const mesh = parent.webserver.meshes[dev.meshid];
|
||||
if (mesh == null) { removeAmtDevice(dev); return false; }
|
||||
if (mesh == null) { removeAmtDevice(dev, 15); return false; }
|
||||
|
||||
// Get the node and change it if needed
|
||||
parent.db.Get(dev.nodeid, function (err, nodes) {
|
||||
@ -660,7 +707,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
|
||||
// Check that the mesh exists
|
||||
const mesh = parent.webserver.meshes[dev.meshid];
|
||||
if (mesh == null) { removeAmtDevice(dev); return; }
|
||||
if (mesh == null) { removeAmtDevice(dev, 16); return; }
|
||||
|
||||
// Get the node and change it if needed
|
||||
parent.db.Get(dev.nodeid, function (err, nodes) {
|
||||
@ -775,7 +822,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
function attemptSyncClockEx(stack, name, response, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to get clock (" + status + ")."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to get clock (" + status + ")."); removeAmtDevice(dev, 17); return; }
|
||||
|
||||
// Compute how much drift between Intel AMT and our clock.
|
||||
var t = new Date(), now = new Date();
|
||||
@ -794,7 +841,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
function attemptSyncClockSet(stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to sync clock (" + status + ")."); removeAmtDevice(dev); }
|
||||
if (status != 200) { dev.consoleMsg("Failed to sync clock (" + status + ")."); removeAmtDevice(dev, 18); }
|
||||
devTaskCompleted(dev)
|
||||
}
|
||||
|
||||
@ -816,7 +863,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
function attemptTlsSyncEx(stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to get security information (" + status + ")."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to get security information (" + status + ")."); removeAmtDevice(dev, 19); return; }
|
||||
|
||||
// Setup the certificates
|
||||
dev.policy.certPrivateKeys = responses['AMT_PublicPrivateKeyPair'].responses;
|
||||
@ -845,18 +892,18 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
dev.amtstack.AMT_PublicKeyManagementService_GenerateKeyPair(0, 2048, function (stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to generate a key pair (" + status + ")."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to generate a key pair (" + status + ")."); removeAmtDevice(dev, 20); return; }
|
||||
|
||||
// Check that we get a key pair reference
|
||||
var x = null;
|
||||
try { x = responses.Body['KeyPair']['ReferenceParameters']['SelectorSet']['Selector']['Value']; } catch (ex) { }
|
||||
if (x == null) { dev.consoleMsg("Unable to get key pair reference."); removeAmtDevice(dev); return; }
|
||||
if (x == null) { dev.consoleMsg("Unable to get key pair reference."); removeAmtDevice(dev, 21); return; }
|
||||
|
||||
// Get the new key pair
|
||||
dev.amtstack.Enum('AMT_PublicPrivateKeyPair', function (stack, name, responses, status, tag) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to get a key pair list (" + status + ")."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to get a key pair list (" + status + ")."); removeAmtDevice(dev, 22); return; }
|
||||
|
||||
// Get the new DER key
|
||||
var DERKey = null;
|
||||
@ -884,7 +931,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
|
||||
// Sign the key pair using the CA certifiate
|
||||
const cert = amtcert_createCertificate(certattributes, xxCaPrivateKey, DERKey, issuerattributes, extKeyUsage);
|
||||
if (cert == null) { dev.consoleMsg("Failed to sign the TLS certificate."); removeAmtDevice(dev); return; }
|
||||
if (cert == null) { dev.consoleMsg("Failed to sign the TLS certificate."); removeAmtDevice(dev, 23); return; }
|
||||
|
||||
// Place the resulting signed certificate back into AMT
|
||||
var pem = obj.parent.certificateOperations.forge.pki.certificateToPem(cert).replace(/(\r\n|\n|\r)/gm, '');
|
||||
@ -897,10 +944,10 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
dev.amtstack.AMT_PublicKeyManagementService_AddCertificate(pem.substring(27, pem.length - 25), function (stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to add TLS certificate (" + status + ")."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to add TLS certificate (" + status + ")."); removeAmtDevice(dev, 24); return; }
|
||||
var certInstanceId = null;
|
||||
try { certInstanceId = responses.Body['CreatedCertificate']['ReferenceParameters']['SelectorSet']['Selector']['Value']; } catch (ex) { }
|
||||
if (certInstanceId == null) { dev.consoleMsg("Failed to get TLS certificate identifier."); removeAmtDevice(dev); return; }
|
||||
if (certInstanceId == null) { dev.consoleMsg("Failed to get TLS certificate identifier."); removeAmtDevice(dev, 25); return; }
|
||||
|
||||
// Set the TLS certificate
|
||||
dev.setTlsSecurityPendingCalls = 3;
|
||||
@ -951,14 +998,14 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
function amtSwitchToTls(stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed setup TLS (" + status + ")."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed setup TLS (" + status + ")."); removeAmtDevice(dev, 26); return; }
|
||||
|
||||
// Check if all the calls are done & perform a commit
|
||||
if ((--dev.setTlsSecurityPendingCalls) == 0) {
|
||||
dev.amtstack.AMT_SetupAndConfigurationService_CommitChanges(null, function (stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed perform commit (" + status + ")."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed perform commit (" + status + ")."); removeAmtDevice(dev, 27); return; }
|
||||
dev.consoleMsg("Enabled TLS, holding 5 seconds...");
|
||||
|
||||
// Update device in the database
|
||||
@ -1112,7 +1159,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
dev.amtstack.AMT_PublicKeyManagementService_AddTrustedRootCertificate(obj.rootCertBase64, function (stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to add server root certificate (" + status + ")."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to add server root certificate (" + status + ")."); removeAmtDevice(dev, 28); return; }
|
||||
dev.consoleMsg("Added server root certificate.");
|
||||
devTaskCompleted(dev);
|
||||
});
|
||||
@ -1165,11 +1212,11 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
|
||||
// We tried 5 times, give up.
|
||||
dev.consoleMsg("Failed to get CIRA state (" + status + ").");
|
||||
removeAmtDevice(dev);
|
||||
removeAmtDevice(dev, 29);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((responses['AMT_UserInitiatedConnectionService'] == null) || (responses['AMT_UserInitiatedConnectionService'].response == null)) { dev.consoleMsg("Invalid CIRA state."); removeAmtDevice(dev); return; }
|
||||
if ((responses['AMT_UserInitiatedConnectionService'] == null) || (responses['AMT_UserInitiatedConnectionService'].response == null)) { dev.consoleMsg("Invalid CIRA state."); removeAmtDevice(dev, 30); return; }
|
||||
|
||||
dev.cira = {};
|
||||
dev.cira.xxRemoteAccess = responses;
|
||||
@ -1251,8 +1298,8 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
dev.amtstack.AMT_RemoteAccessService_AddMpServer(dev.cira.mpsName, dev.cira.mpsAddressFormat, dev.cira.mpsPort, 2, null, dev.cira.meshidx, dev.cira.mpsPass, dev.cira.mpsName, function (stack, name, response, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to create new MPS server (" + status + ")."); removeAmtDevice(dev); return; }
|
||||
if ((response.Body.MpServer == null) || (response.Body.MpServer.ReferenceParameters == null) || (response.Body.MpServer.ReferenceParameters.SelectorSet == null) || (response.Body.MpServer.ReferenceParameters.SelectorSet.Selector == null)) { dev.consoleMsg("Create new MPS server invalid response."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to create new MPS server (" + status + ")."); removeAmtDevice(dev, 31); return; }
|
||||
if ((response.Body.MpServer == null) || (response.Body.MpServer.ReferenceParameters == null) || (response.Body.MpServer.ReferenceParameters.SelectorSet == null) || (response.Body.MpServer.ReferenceParameters.SelectorSet.Selector == null)) { dev.consoleMsg("Create new MPS server invalid response."); removeAmtDevice(dev, 32); return; }
|
||||
dev.cira.mpsPresent = getItem(response.Body.MpServer.ReferenceParameters.SelectorSet.Selector, '@Name', 'Name').Value;
|
||||
dev.consoleMsg("Created new MPS server.");
|
||||
addMpsPolicy(dev);
|
||||
@ -1292,7 +1339,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
dev.amtstack.AMT_RemoteAccessService_AddRemoteAccessPolicyRule(trigger, 0, extendedData, ciraServers, cilaServers, function (stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to create new MPS policy (" + status + ")."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to create new MPS policy (" + status + ")."); removeAmtDevice(dev, 33); return; }
|
||||
dev.consoleMsg("Created new MPS policy.");
|
||||
checkEnvironmentDetection(dev);
|
||||
});
|
||||
@ -1339,7 +1386,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
dev.amtstack.Put('AMT_EnvironmentDetectionSettingData', t, function (stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to set environement detection (" + status + ")."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to set environement detection (" + status + ")."); removeAmtDevice(dev, 34); return; }
|
||||
if (dev.cira.envclear) { dev.consoleMsg("Environment detection cleared."); } else { dev.consoleMsg("Environment detection set."); }
|
||||
devTaskCompleted(dev);
|
||||
}, 0, 1);
|
||||
@ -1453,7 +1500,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
function attemptFetchHardwareInventory(dev, func) {
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
const mesh = parent.webserver.meshes[dev.meshid];
|
||||
if (mesh == null) { removeAmtDevice(dev); return; }
|
||||
if (mesh == null) { removeAmtDevice(dev, 35); return; }
|
||||
if (mesh.mtype == 1) { // If this is a Intel AMT only device group, pull the hardware inventory and network information for this device
|
||||
dev.consoleMsg("Fetching hardware inventory.");
|
||||
dev.taskCount = 2;
|
||||
@ -1640,12 +1687,12 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
function activateIntelAmt(dev) {
|
||||
// Find the Intel AMT policy
|
||||
const mesh = parent.webserver.meshes[dev.meshid];
|
||||
if (mesh == null) { dev.consoleMsg("Unable to find device group."); removeAmtDevice(dev); return false; }
|
||||
if (mesh == null) { dev.consoleMsg("Unable to find device group."); removeAmtDevice(dev, 36); return false; }
|
||||
var amtPolicy = 0; // 0 = Do nothing, 1 = Deactivate CCM, 2 = CCM, 3 = ACM
|
||||
var ccmPolicy = 0; // Only used when in ACM policy: 0 = Do nothing, 1 = Deactivate CCM, 2 = CCM is ACM fails
|
||||
if (mesh.amt != null) { if (typeof mesh.amt.type == 'number') { amtPolicy = mesh.amt.type; } if (typeof mesh.amt.ccm == 'number') { ccmPolicy = mesh.amt.ccm; } }
|
||||
if ((typeof dev.mpsConnection.tag.meiState.OsAdmin != 'object') || (typeof dev.mpsConnection.tag.meiState.OsAdmin.user != 'string') || (typeof dev.mpsConnection.tag.meiState.OsAdmin.pass != 'string')) { amtPolicy = 0; }
|
||||
if (amtPolicy == 0) { removeAmtDevice(dev); return false; } // Do nothing, we should not have gotten this CIRA-LMS connection.
|
||||
if (amtPolicy == 0) { removeAmtDevice(dev, 37); return false; } // Do nothing, we should not have gotten this CIRA-LMS connection.
|
||||
if (amtPolicy == 2) { activateIntelAmtCcm(dev, mesh.amt.password); } // Activate to CCM policy
|
||||
if ((amtPolicy == 3) || (amtPolicy == 4)) { // Activate to ACM policy
|
||||
var acminfo = checkAcmActivation(dev);
|
||||
@ -1657,7 +1704,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
} else {
|
||||
// We are not in CCM, go to CCM now
|
||||
if ((amtPolicy == 4) || ((amtPolicy == 3) && (ccmPolicy == 2))) { activateIntelAmtCcm(dev, mesh.amt.password); } // If we are in full automatic or ACM with CCM allowed, setup CCM.
|
||||
else { removeAmtDevice(dev); return false; } // We are not in CCM and policy restricts use of CCM, so exit now.
|
||||
else { dev.consoleMsg("No opportunity for ACM activation."); removeAmtDevice(dev, 38); return false; } // We are not in CCM and policy restricts use of CCM, so exit now.
|
||||
}
|
||||
} else {
|
||||
// Found a certificate to activate to ACM.
|
||||
@ -1666,7 +1713,15 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
deactivateIntelAmtCCM(dev);
|
||||
} else {
|
||||
// We are not activated now, go to ACM directly.
|
||||
activateIntelAmtAcm(dev, mesh.amt.password, acminfo);
|
||||
// If this is Intel AMT 14 or better, we are going to attempt a host-based end-to-end TLS activation.
|
||||
if (typeof dev.intelamt.ver == 'string') { var verSplit = dev.intelamt.ver.split('.'); if (verSplit.length >= 3) { dev.aquired.majorver = parseInt(verSplit[0]); dev.aquired.minorver = parseInt(verSplit[1]); } }
|
||||
//if (dev.aquired.majorver >= 14) {
|
||||
// Perform host-based TLS ACM activation
|
||||
//activateIntelAmtTlsAcm(dev, mesh.amt.password, acminfo);
|
||||
//} else {
|
||||
// Perform host-based ACM activation
|
||||
activateIntelAmtAcm(dev, mesh.amt.password, acminfo);
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1689,14 +1744,14 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
function activateIntelAmtCcmEx1(stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to get Intel AMT state."); removeAmtDevice(dev); return; }
|
||||
if (responses['IPS_HostBasedSetupService'].response['AllowedControlModes'].length != 2) { dev.consoleMsg("Client control mode activation not allowed."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to get Intel AMT state."); removeAmtDevice(dev, 39); return; }
|
||||
if (responses['IPS_HostBasedSetupService'].response['AllowedControlModes'].length != 2) { dev.consoleMsg("Client control mode activation not allowed."); removeAmtDevice(dev, 40); return; }
|
||||
|
||||
// Log the activation request, logging is a required step for activation.
|
||||
var domain = parent.config.domains[dev.domainid];
|
||||
if (domain == null) { dev.consoleMsg("Invalid domain."); removeAmtDevice(dev); return; }
|
||||
if (domain == null) { dev.consoleMsg("Invalid domain."); removeAmtDevice(dev, 41); return; }
|
||||
if (parent.certificateOperations.logAmtActivation(domain, { time: new Date(), action: 'ccmactivate', domain: dev.domainid, amtUuid: dev.mpsConnection.tag.meiState.UUID, amtRealm: responses['AMT_GeneralSettings'].response['DigestRealm'], user: 'admin', password: dev.temp.pass, ipport: dev.mpsConnection.remoteAddr + ':' + dev.mpsConnection.remotePort, nodeid: dev.nodeid, meshid: dev.meshid, computerName: dev.name }) == false) {
|
||||
dev.consoleMsg("Unable to log operation."); removeAmtDevice(dev); return;
|
||||
dev.consoleMsg("Unable to log operation."); removeAmtDevice(dev, 42); return;
|
||||
}
|
||||
|
||||
// Perform CCM activation
|
||||
@ -1706,7 +1761,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
function activateIntelAmtCcmEx2(stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to activate Intel AMT to CCM."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to activate Intel AMT to CCM."); removeAmtDevice(dev, 43); return; }
|
||||
|
||||
// Update the device
|
||||
dev.aquired = {};
|
||||
@ -1769,11 +1824,78 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
return null; // Did not find a match
|
||||
}
|
||||
|
||||
// Attempt Intel AMT TLS ACM activation
|
||||
function activateIntelAmtTlsAcm(dev, password, acminfo) {
|
||||
// Check if MeshAgent/MeshCMD can support the startConfigurationhostB() call.
|
||||
if ((dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null) && (typeof dev.mpsConnection.tag.meiState['core-ver'] == 'number') && (dev.mpsConnection.tag.meiState['core-ver'] > 0)) {
|
||||
// Generate a random Intel AMT password if needed
|
||||
if ((password == null) || (password == '')) { password = getRandomAmtPassword(); }
|
||||
dev.temp = { pass: password, acminfo: acminfo };
|
||||
|
||||
// Get our ACM activation certificate chain
|
||||
var acmTlsInfo = parent.certificateOperations.getAcmCertChain(parent.config.domains[dev.domainid], dev.temp.acminfo.fqdn, dev.temp.acminfo.hash);
|
||||
if (acmTlsInfo.error == 1) { dev.consoleMsg(acmTlsInfo.errorText); removeAmtDevice(dev, 44); return; }
|
||||
dev.acmTlsInfo = acmTlsInfo;
|
||||
|
||||
// Send the MEI command to enable TLS connections
|
||||
dev.consoleMsg("Performing TLS ACM activation...");
|
||||
dev.controlMsg({ action: 'startTlsHostConfig', hash: acmTlsInfo.hash, hostVpn: false, dnsSuffixList: null });
|
||||
} else {
|
||||
// MeshCore or MeshCMD is to old
|
||||
dev.consoleMsg("This software is to old to support ACM activation, pleasse update and try again.");
|
||||
removeAmtDevice(dev);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt Intel AMT TLS ACM activation after startConfiguration() is called on remote device
|
||||
function activateIntelAmtTlsAcmEx(dev, startConfigData) {
|
||||
console.log('activateIntelAmtTlsAcmEx', dev.mpsConnection.tag.meiState.OsAdmin.user, dev.mpsConnection.tag.meiState.OsAdmin.pass);
|
||||
|
||||
// Setup the WSMAN stack, no TLS
|
||||
var comm = CreateWsmanComm(dev.nodeid, 16993, 'admin', '', 1, { cert: dev.acmTlsInfo.certs, key: dev.acmTlsInfo.signkey }, dev.mpsConnection); // TLS with client certificate chain and key.
|
||||
// TODO: Intel AMT leaf TLS cert need to SHA256 hash to "startConfigData.hash"
|
||||
var wsstack = WsmanStackCreateService(comm);
|
||||
dev.amtstack = AmtStackCreateService(wsstack);
|
||||
dev.amtstack.dev = dev;
|
||||
dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], activateIntelAmtTlsAcmEx1);
|
||||
}
|
||||
|
||||
function activateIntelAmtTlsAcmEx1(stack, name, responses, status) {
|
||||
console.log('activateIntelAmtTlsAcmEx1', status, responses);
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) {
|
||||
dev.consoleMsg("Failed to perform ACM TLS connection, falling back to legacy host-based activation.");
|
||||
activateIntelAmtAcm(dev); // Falling back to legacy WSMAN ACM activation, start by refreshing $$OsAdmin username and password.
|
||||
} else {
|
||||
// TODO!!!
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt Intel AMT ACM activation
|
||||
function activateIntelAmtAcm(dev, password, acminfo) {
|
||||
// Generate a random Intel AMT password if needed
|
||||
if ((password == null) || (password == '')) { password = getRandomAmtPassword(); }
|
||||
dev.temp = { pass: password, acminfo: acminfo };
|
||||
// Check if MeshAgent/MeshCMD can support the stopConfiguration() call.
|
||||
if ((dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null) && (typeof dev.mpsConnection.tag.meiState['core-ver'] == 'number') && (dev.mpsConnection.tag.meiState['core-ver'] > 0)) {
|
||||
// Generate a random Intel AMT password if needed
|
||||
if (acminfo != null) {
|
||||
if ((password == null) || (password == '')) { password = getRandomAmtPassword(); }
|
||||
dev.temp = { pass: password, acminfo: acminfo };
|
||||
}
|
||||
dev.acmactivate = 1;
|
||||
|
||||
// Send the MEI command to stop configuration.
|
||||
// If Intel AMT is "in-provisioning" mode, the WSMAN ACM activation will not work, so we need to do this first.
|
||||
dev.consoleMsg("Getting ready for ACM activation...");
|
||||
dev.controlMsg({ action: 'stopConfiguration' });
|
||||
} else {
|
||||
// MeshCore or MeshCMD is to old
|
||||
dev.consoleMsg("This software is to old to support ACM activation, pleasse update and try again.");
|
||||
removeAmtDevice(dev);
|
||||
}
|
||||
}
|
||||
|
||||
function activateIntelAmtAcmEx0(dev) {
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
|
||||
// Setup the WSMAN stack, no TLS
|
||||
var comm = CreateWsmanComm(dev.nodeid, 16992, dev.mpsConnection.tag.meiState.OsAdmin.user, dev.mpsConnection.tag.meiState.OsAdmin.pass, 0, null, dev.mpsConnection); // No TLS
|
||||
@ -1786,13 +1908,13 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
function activateIntelAmtAcmEx1(stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to get Intel AMT state."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to get Intel AMT state."); removeAmtDevice(dev, 46); return; }
|
||||
|
||||
// Sign the Intel AMT ACM activation request
|
||||
var info = { nonce: responses['IPS_HostBasedSetupService'].response['ConfigurationNonce'], realm: responses['AMT_GeneralSettings'].response['DigestRealm'], fqdn: dev.temp.acminfo.fqdn, hash: dev.temp.acminfo.hash, uuid: dev.mpsConnection.tag.meiState.UUID };
|
||||
var acmdata = parent.certificateOperations.signAcmRequest(parent.config.domains[dev.domainid], info, 'admin', dev.temp.pass, dev.mpsConnection.remoteAddr + ':' + dev.mpsConnection.remotePort, dev.nodeid, dev.meshid, dev.name, 0);
|
||||
if (acmdata == null) { dev.consoleMsg("Failed to sign ACM nonce."); removeAmtDevice(dev); return; }
|
||||
if (acmdata.error != null) { dev.consoleMsg(acmdata.errorText); removeAmtDevice(dev); return; }
|
||||
if (acmdata == null) { dev.consoleMsg("Failed to sign ACM nonce."); removeAmtDevice(dev, 47); return; }
|
||||
if (acmdata.error != null) { dev.consoleMsg(acmdata.errorText); removeAmtDevice(dev, 48); return; }
|
||||
|
||||
// Log this activation event
|
||||
var event = { etype: 'node', action: 'amtactivate', nodeid: dev.nodeid, domain: dev.domainid, msgid: 58, msgArgs: [ dev.temp.acminfo.fqdn ], msg: 'Device requested Intel(R) AMT ACM activation, FQDN: ' + dev.temp.acminfo.fqdn };
|
||||
@ -1815,8 +1937,8 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
function (stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to set ACM certificate chain (" + status + ")."); removeAmtDevice(dev); return; }
|
||||
if (responses['Body']['ReturnValue'] != 0) { dev.consoleMsg("Failed to set ACM certificate chain (ERR/" + responses['Body']['ReturnValue'] + ")."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to set ACM certificate chain (" + status + ")."); removeAmtDevice(dev, 49); return; }
|
||||
if (responses['Body']['ReturnValue'] != 0) { dev.consoleMsg("Failed to set ACM certificate chain (ERR/" + responses['Body']['ReturnValue'] + ")."); removeAmtDevice(dev, 50); return; }
|
||||
|
||||
// Move to the next activation operation
|
||||
dev.temp.acmdata.index++;
|
||||
@ -1828,8 +1950,8 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
function (stack, name, responses, status) {
|
||||
const dev = stack.dev;
|
||||
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
|
||||
if (status != 200) { dev.consoleMsg("Failed to complete ACM activation (" + status + ")."); removeAmtDevice(dev); return; }
|
||||
if (responses['Body']['ReturnValue'] != 0) { dev.consoleMsg("Failed to complete ACM activation (ERR/" + responses['Body']['ReturnValue'] + ")."); removeAmtDevice(dev); return; }
|
||||
if (status != 200) { dev.consoleMsg("Failed to complete ACM activation (" + status + ")."); removeAmtDevice(dev, 51); return; }
|
||||
if (responses['Body']['ReturnValue'] != 0) { dev.consoleMsg("Failed to complete ACM activation (ERR/" + responses['Body']['ReturnValue'] + ")."); removeAmtDevice(dev, 52); return; }
|
||||
|
||||
// Success, switch to managing this device
|
||||
obj.parent.mpsserver.SendJsonControl(dev.mpsConnection, { action: 'mestate' }); // Request an MEI state refresh
|
||||
@ -1879,7 +2001,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
function deactivateIntelAmtCCMEx(dev, state) {
|
||||
if (state != 0) {
|
||||
dev.consoleMsg("Failed to deactivate Intel AMT CCM.");
|
||||
removeAmtDevice(dev);
|
||||
removeAmtDevice(dev, 53);
|
||||
} else {
|
||||
// Update the device
|
||||
dev.aquired = {};
|
||||
@ -1892,7 +2014,7 @@ module.exports.CreateAmtManager = function (parent) {
|
||||
if (dev.policy.amtPolicy == 1) { // Deactivation policy, we are done.
|
||||
dev.consoleMsg("Deactivation successful.");
|
||||
dev.consoleMsg("Done.");
|
||||
removeAmtDevice(dev);
|
||||
removeAmtDevice(dev, 54);
|
||||
} else {
|
||||
// Wait 20 seconds before attempting any operation on this device
|
||||
dev.consoleMsg("Deactivation successful, holding for 1 minute...");
|
||||
|
@ -287,15 +287,18 @@ module.exports.CreateAmtScanner = function (parent) {
|
||||
obj.changeConnectState = function (tag, minorVersion, majorVersion, provisioningState, openPort, dualPorts, rinfo, user) {
|
||||
//var provisioningStates = { 0: 'Pre', 1: 'in', 2: 'Post' };
|
||||
//var provisioningStateStr = provisioningStates[provisioningState];
|
||||
//console.log('Intel AMT ' + majorVersion + '.' + minorVersion + ', ' + provisioningStateStr + '-Provisioning at ' + rinfo.address + ', Open Ports: [' + openPort + '], tag: ' + tag);
|
||||
//console.log('Intel AMT ' + majorVersion + '.' + minorVersion + ', ' + provisioningStateStr + '-Provisioning at ' + rinfo.address + ', Open Ports: [' + openPort + '], tag: ' + tag + ', dualPorts: ' + dualPorts);
|
||||
var scaninfo = obj.scanTableTags[tag];
|
||||
if (scaninfo != undefined) {
|
||||
scaninfo.lastpong = Date.now();
|
||||
if (scaninfo.state == 0) {
|
||||
scaninfo.state = 1;
|
||||
scaninfo.nodeinfo.intelamt.tls = (((openPort == 16993) || (dualPorts == true)) ? 1 : 0);
|
||||
scaninfo.nodeinfo.intelamt.ver = majorVersion + '.' + minorVersion;
|
||||
scaninfo.nodeinfo.intelamt.state = provisioningState;
|
||||
if ((openPort == 16993) || (dualPorts == true)) { scaninfo.nodeinfo.intelamt.tls = 1; }
|
||||
else if (openPort == 16992) { scaninfo.nodeinfo.intelamt.tls = 0; }
|
||||
if (majorVersion > 0) { // Older versions of Intel AMT report the AMT version.
|
||||
scaninfo.nodeinfo.intelamt.ver = majorVersion + '.' + minorVersion;
|
||||
scaninfo.nodeinfo.intelamt.state = provisioningState;
|
||||
}
|
||||
obj.parent.SetConnectivityState(scaninfo.nodeinfo.meshid, scaninfo.nodeinfo._id, scaninfo.lastpong, 4, 7); // Report power state as "present" (7).
|
||||
obj.changeAmtState(scaninfo.nodeinfo._id, scaninfo.nodeinfo.intelamt.ver, provisioningState, scaninfo.nodeinfo.intelamt.tls);
|
||||
if (obj.parent.amtManager != null) { obj.parent.amtManager.startAmtManagement(scaninfo.nodeinfo._id, 3, scaninfo.nodeinfo.host); }
|
||||
|
@ -28,6 +28,43 @@ module.exports.CertificateOperations = function (parent) {
|
||||
|
||||
const TopLevelDomainExtendedSupport = { 'net': 2, 'com': 2, 'arpa': 3, 'org': 2, 'gov': 2, 'edu': 2, 'de': 2, 'fr': 3, 'cn': 3, 'nl': 3, 'br': 3, 'mx': 3, 'uk': 3, 'pl': 3, 'tw': 3, 'ca': 3, 'fi': 3, 'be': 3, 'ru': 3, 'se': 3, 'ch': 2, 'dk': 2, 'ar': 3, 'es': 3, 'no': 3, 'at': 3, 'in': 3, 'tr': 3, 'cz': 2, 'ro': 3, 'hu': 3, 'nz': 3, 'pt': 3, 'il': 3, 'gr': 3, 'co': 3, 'ie': 3, 'za': 3, 'th': 3, 'sg': 3, 'hk': 3, 'cl': 2, 'lt': 3, 'id': 3, 'hr': 3, 'ee': 3, 'bg': 3, 'ua': 2 };
|
||||
|
||||
// Sign a Intel AMT TLS ACM activation request
|
||||
obj.getAcmCertChain = function (domain, fqdn, hash) {
|
||||
if ((domain == null) || (domain.amtacmactivation == null) || (domain.amtacmactivation.certs == null) || (fqdn == null) || (hash == null)) return { action: 'acmactivate', error: 1, errorText: 'Invalid arguments' };
|
||||
if (parent.common.validateString(fqdn, 4, 256) == false) return { action: 'acmactivate', error: 1, errorText: "Invalid FQDN argument." };
|
||||
if (parent.common.validateString(hash, 16, 256) == false) return { action: 'acmactivate', error: 1, errorText: "Invalid hash argument." };
|
||||
|
||||
// Look for the signing certificate
|
||||
var signkey = null, certChain = null, hashAlgo = null, certIndex = null;
|
||||
for (var i in domain.amtacmactivation.certs) {
|
||||
const certEntry = domain.amtacmactivation.certs[i];
|
||||
if ((certEntry.sha256 == hash) && ((certEntry.cn == '*') || (certEntry.cn == fqdn))) { hashAlgo = 'sha256'; signkey = certEntry.key; certChain = certEntry.certs; certIndex = i; break; }
|
||||
if ((certEntry.sha1 == hash) && ((certEntry.cn == '*') || (certEntry.cn == fqdn))) { hashAlgo = 'sha1'; signkey = certEntry.key; certChain = certEntry.certs; certIndex = i; break; }
|
||||
}
|
||||
if (signkey == null) return { action: 'acmactivate', error: 2, errorText: "No signing certificate found." }; // Did not find a match.
|
||||
|
||||
// If the matching certificate our wildcard root cert, we can use the root to match any FQDN
|
||||
if (domain.amtacmactivation.certs[certIndex].cn == '*') {
|
||||
// Create a leaf certificate that matches the FQDN we want
|
||||
// TODO: This is an expensive operation, work on ways to pre-generate or cache this leaf certificate.
|
||||
var rootcert = { cert: domain.amtacmactivation.certs[certIndex].rootcert, key: obj.pki.privateKeyFromPem(domain.amtacmactivation.certs[certIndex].key) };
|
||||
var leafcert = obj.IssueWebServerCertificate(rootcert, false, fqdn, 'mc', 'Intel(R) Client Setup Certificate', { serverAuth: true, '2.16.840.1.113741.1.2.3': true }, false);
|
||||
|
||||
// Setup the certificate chain and key
|
||||
//certChain = [ obj.pki.certificateToPem(leafcert.cert), obj.pki.certificateToPem(domain.amtacmactivation.certs[certIndex].rootcert) ];
|
||||
certChain = [ obj.pki.certificateToPem(domain.amtacmactivation.certs[certIndex].rootcert), obj.pki.certificateToPem(leafcert.cert) ];
|
||||
signkey = obj.pki.privateKeyToPem(leafcert.key);
|
||||
} else {
|
||||
// Make sure the cert chain is in PEM format
|
||||
var certChain2 = [];
|
||||
for (var i in certChain) { certChain2.push("-----BEGIN CERTIFICATE-----\r\n" + certChain[i] + "\r\n-----END CERTIFICATE-----\r\n"); }
|
||||
certChain = certChain2;
|
||||
}
|
||||
|
||||
// Hash the leaf certificate and return the certificate chain and signing key
|
||||
return { action: 'acmactivate', certs: certChain, signkey: signkey, hash: obj.getCertHash(certChain[certChain.length - 1]) };
|
||||
}
|
||||
|
||||
// Sign a Intel AMT ACM activation request
|
||||
obj.signAcmRequest = function (domain, request, user, pass, ipport, nodeid, meshid, computerName, agentId) {
|
||||
if ((domain == null) || (domain.amtacmactivation == null) || (domain.amtacmactivation.certs == null) || (request == null) || (request.nonce == null) || (request.realm == null) || (request.fqdn == null) || (request.hash == null)) return { 'action': 'acmactivate', 'error': 1, 'errorText': 'Invalid arguments' };
|
||||
|
21
common.js
21
common.js
@ -292,4 +292,25 @@ module.exports.meshServerRightsArrayToNumber = function (val) {
|
||||
return newAccRights;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Validate an object to make sure it can be stored in MongoDB
|
||||
module.exports.validateObjectForMongo = function (obj, maxStrLen) {
|
||||
return validateObjectForMongoRec(obj, maxStrLen);
|
||||
}
|
||||
|
||||
function validateObjectForMongoRec(obj, maxStrLen) {
|
||||
if (typeof obj != 'object') return false;
|
||||
for (var i in obj) {
|
||||
// Check the key name is not too long
|
||||
if (i.length > 100) return false;
|
||||
// Check if all chars are alpha-numeric or underscore.
|
||||
for (var j in i) { const c = i.charCodeAt(j); if ((c < 48) || ((c > 57) && (c < 65)) || ((c > 90) && (c < 97) && (c != 95)) || (c > 122)) return false; }
|
||||
// If the value is a string, check it's not too long
|
||||
if ((typeof obj[i] == 'string') && (obj[i].length > maxStrLen)) return false;
|
||||
// If the value is an object, check it.
|
||||
if ((typeof obj[i] == 'object') && (Array.isArray(obj[i]) == false) && (validateObjectForMongoRec(obj[i], maxStrLen) == false)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
67
db.js
67
db.js
@ -38,9 +38,26 @@ module.exports.CreateDB = function (parent, func) {
|
||||
obj.dbRecordsDecryptKey = null;
|
||||
obj.changeStream = false;
|
||||
obj.pluginsActive = ((parent.config) && (parent.config.settings) && (parent.config.settings.plugins != null) && (parent.config.settings.plugins != false) && ((typeof parent.config.settings.plugins != 'object') || (parent.config.settings.plugins.enabled != false)));
|
||||
obj.dbCounters = {
|
||||
fileSet: 0,
|
||||
fileRemove: 0,
|
||||
powerSet: 0,
|
||||
eventsSet: 0
|
||||
}
|
||||
|
||||
// MongoDB bulk operations state
|
||||
if (parent.config.settings.mongodbbulkoperations) {
|
||||
// Added counters
|
||||
obj.dbCounters.fileSetPending = 0;
|
||||
obj.dbCounters.fileSetBulk = 0;
|
||||
obj.dbCounters.fileRemovePending = 0;
|
||||
obj.dbCounters.fileRemoveBulk = 0;
|
||||
obj.dbCounters.powerSetPending = 0;
|
||||
obj.dbCounters.powerSetBulk = 0;
|
||||
obj.dbCounters.eventsSetPending = 0;
|
||||
obj.dbCounters.eventsSetBulk = 0;
|
||||
|
||||
/// Added bulk accumulators
|
||||
obj.filePendingGet = null;
|
||||
obj.filePendingGets = null;
|
||||
obj.filePendingRemove = null;
|
||||
@ -514,12 +531,15 @@ module.exports.CreateDB = function (parent, func) {
|
||||
|
||||
// Setup the changeStream on the MongoDB main collection if possible
|
||||
if (parent.args.mongodbchangestream == true) {
|
||||
obj.dbCounters.changeStream = { change: 0, update: 0, insert: 0, delete: 0 };
|
||||
if (typeof obj.file.watch != 'function') {
|
||||
console.log('WARNING: watch() is not a function, MongoDB ChangeStream not supported.');
|
||||
} else {
|
||||
obj.fileChangeStream = obj.file.watch([{ $match: { $or: [{ 'fullDocument.type': { $in: ['node', 'mesh', 'user', 'ugrp'] } }, { 'operationType': 'delete' }] } }], { fullDocument: 'updateLookup' });
|
||||
obj.fileChangeStream.on('change', function (change) {
|
||||
obj.dbCounters.changeStream.change++;
|
||||
if ((change.operationType == 'update') || (change.operationType == 'replace')) {
|
||||
obj.dbCounters.changeStream.update++;
|
||||
switch (change.fullDocument.type) {
|
||||
case 'node': { dbNodeChange(change, false); break; } // A node has changed
|
||||
case 'mesh': { dbMeshChange(change, false); break; } // A device group has changed
|
||||
@ -527,6 +547,7 @@ module.exports.CreateDB = function (parent, func) {
|
||||
case 'ugrp': { dbUGrpChange(change, false); break; } // A user account has changed
|
||||
}
|
||||
} else if (change.operationType == 'insert') {
|
||||
obj.dbCounters.changeStream.insert++;
|
||||
switch (change.fullDocument.type) {
|
||||
case 'node': { dbNodeChange(change, true); break; } // A node has added
|
||||
case 'mesh': { dbMeshChange(change, true); break; } // A device group has created
|
||||
@ -534,6 +555,7 @@ module.exports.CreateDB = function (parent, func) {
|
||||
case 'ugrp': { dbUGrpChange(change, true); break; } // A user account has created
|
||||
}
|
||||
} else if (change.operationType == 'delete') {
|
||||
obj.dbCounters.changeStream.delete++;
|
||||
if ((change.documentKey == null) || (change.documentKey._id == null)) return;
|
||||
var splitId = change.documentKey._id.split('/');
|
||||
switch (splitId[0]) {
|
||||
@ -898,6 +920,7 @@ module.exports.CreateDB = function (parent, func) {
|
||||
if ((obj.databaseType == 4) || (obj.databaseType == 5)) {
|
||||
// Database actions on the main collection (MariaDB or MySQL)
|
||||
obj.Set = function (value, func) {
|
||||
obj.dbCounters.fileSet++;
|
||||
var extra = null, extraex = null;
|
||||
value = common.escapeLinksFieldNameEx(value);
|
||||
if (value.meshid) { extra = value.meshid; } else if (value.email) { extra = 'email/' + value.email; } else if (value.nodeid) { extra = value.nodeid; }
|
||||
@ -947,6 +970,7 @@ module.exports.CreateDB = function (parent, func) {
|
||||
// Database actions on the events collection
|
||||
obj.GetAllEvents = function (func) { sqlDbQuery('SELECT doc FROM meshcentral.events', null, func); };
|
||||
obj.StoreEvent = function (event, func) {
|
||||
obj.dbCounters.eventsSet++;
|
||||
var batchQuery = [['INSERT INTO meshcentral.events VALUE (?, ?, ?, ?, ?, ?, ?)', [null, event.time, ((typeof event.domain == 'string') ? event.domain : null), event.action, event.nodeid ? event.nodeid : null, event.userid ? event.userid : null, JSON.stringify(event)]]];
|
||||
for (var i in event.ids) { if (event.ids[i] != '*') { batchQuery.push(['INSERT INTO meshcentral.eventids VALUE (LAST_INSERT_ID(), ?)', [event.ids[i]]]); } }
|
||||
sqlDbBatchExec(batchQuery, function (err, docs) { if (func != null) { func(err, docs); } });
|
||||
@ -981,6 +1005,7 @@ module.exports.CreateDB = function (parent, func) {
|
||||
sqlDbQuery('SELECT doc FROM meshcentral.events JOIN meshcentral.eventids ON id = fkid WHERE (domain = ? AND userid = ? AND target IN (?)) GROUP BY id ORDER BY time DESC LIMIT ?', [domain, userid, ids, limit], func);
|
||||
}
|
||||
};
|
||||
//obj.GetUserLoginEvents = function (domain, username, func) { } // TODO
|
||||
obj.GetNodeEventsWithLimit = function (nodeid, domain, limit, func) { sqlDbQuery('SELECT doc FROM meshcentral.events WHERE (nodeid = ?) AND (domain = ?) ORDER BY time DESC LIMIT ?', [nodeid, domain, limit], func); };
|
||||
obj.GetNodeEventsSelfWithLimit = function (nodeid, domain, userid, limit, func) { sqlDbQuery('SELECT doc FROM meshcentral.events WHERE (nodeid = ?) AND (domain = ?) AND ((userid = ?) OR (userid IS NULL)) ORDER BY time DESC LIMIT ?', [nodeid, domain, userid, limit], func); };
|
||||
obj.RemoveAllEvents = function (domain) { sqlDbQuery('DELETE FROM meshcentral.events', null, function (err, docs) { }); };
|
||||
@ -990,7 +1015,7 @@ module.exports.CreateDB = function (parent, func) {
|
||||
|
||||
// Database actions on the power collection
|
||||
obj.getAllPower = function (func) { sqlDbQuery('SELECT doc FROM meshcentral.power', null, func); };
|
||||
obj.storePowerEvent = function (event, multiServer, func) { if (multiServer != null) { event.server = multiServer.serverid; } sqlDbQuery('INSERT INTO meshcentral.power VALUE (?, ?, ?, ?)', [null, event.time, event.nodeid ? event.nodeid : null, JSON.stringify(event)], func); };
|
||||
obj.storePowerEvent = function (event, multiServer, func) { obj.dbCounters.powerSet++; if (multiServer != null) { event.server = multiServer.serverid; } sqlDbQuery('INSERT INTO meshcentral.power VALUE (?, ?, ?, ?)', [null, event.time, event.nodeid ? event.nodeid : null, JSON.stringify(event)], func); };
|
||||
obj.getPowerTimeline = function (nodeid, func) { sqlDbQuery('SELECT doc FROM meshcentral.power WHERE ((nodeid = ?) OR (nodeid = "*")) ORDER BY time DESC', [nodeid], func); };
|
||||
obj.removeAllPowerEvents = function () { sqlDbQuery('DELETE FROM meshcentral.power', null, function (err, docs) { }); };
|
||||
obj.removeAllPowerEventsForNode = function (nodeid) { sqlDbQuery('DELETE FROM meshcentral.power WHERE nodeid = ?', [nodeid], function (err, docs) { }); };
|
||||
@ -1054,11 +1079,13 @@ module.exports.CreateDB = function (parent, func) {
|
||||
obj.Set = function (data, func) { // Fast Set operation using bulkWrite(), this is much faster then using replaceOne()
|
||||
if (obj.filePendingSet == false) {
|
||||
// Perform the operation now
|
||||
obj.dbCounters.fileSet++;
|
||||
obj.filePendingSet = true; obj.filePendingSets = null;
|
||||
if (func != null) { obj.filePendingCbs = [func]; }
|
||||
obj.file.bulkWrite([{ replaceOne: { filter: { _id: data._id }, replacement: performTypedRecordEncrypt(common.escapeLinksFieldNameEx(data)), upsert: true } }], fileBulkWriteCompleted);
|
||||
} else {
|
||||
// Add this operation to the pending list
|
||||
obj.dbCounters.fileSetPending++;
|
||||
if (obj.filePendingSets == null) { obj.filePendingSets = {} }
|
||||
obj.filePendingSets[data._id] = data;
|
||||
if (func != null) { if (obj.filePendingCb == null) { obj.filePendingCb = [func]; } else { obj.filePendingCb.push(func); } }
|
||||
@ -1092,7 +1119,11 @@ module.exports.CreateDB = function (parent, func) {
|
||||
}
|
||||
};
|
||||
} else {
|
||||
obj.Set = function (data, func) { data = common.escapeLinksFieldNameEx(data); obj.file.replaceOne({ _id: data._id }, performTypedRecordEncrypt(data), { upsert: true }, func); };
|
||||
obj.Set = function (data, func) {
|
||||
obj.dbCounters.fileSet++;
|
||||
data = common.escapeLinksFieldNameEx(data);
|
||||
obj.file.replaceOne({ _id: data._id }, performTypedRecordEncrypt(data), { upsert: true }, func);
|
||||
};
|
||||
obj.Get = function (id, func) {
|
||||
if (arguments.length > 2) {
|
||||
var parms = [func];
|
||||
@ -1144,18 +1175,20 @@ module.exports.CreateDB = function (parent, func) {
|
||||
if (parent.config.settings.mongodbbulkoperations) {
|
||||
obj.Remove = function (id, func) { // Fast remove operation using a bulk find() to reduce round trips to the database.
|
||||
if (obj.filePendingRemoves == null) {
|
||||
// No pending gets, perform the operation now.
|
||||
// No pending removes, perform the operation now.
|
||||
obj.dbCounters.fileRemove++;
|
||||
obj.filePendingRemoves = {};
|
||||
obj.filePendingRemoves[id] = [func];
|
||||
obj.file.deleteOne({ _id: id }, fileBulkRemoveCompleted);
|
||||
} else {
|
||||
// Add remove to pending list.
|
||||
obj.dbCounters.fileRemovePending++;
|
||||
if (obj.filePendingRemove == null) { obj.filePendingRemove = {}; }
|
||||
if (obj.filePendingRemove[id] == null) { obj.filePendingRemove[id] = [func]; } else { obj.filePendingRemove[id].push(func); }
|
||||
}
|
||||
};
|
||||
} else {
|
||||
obj.Remove = function (id, func) { obj.file.deleteOne({ _id: id }, func); };
|
||||
obj.Remove = function (id, func) { obj.dbCounters.fileRemove++; obj.file.deleteOne({ _id: id }, func); };
|
||||
}
|
||||
|
||||
obj.RemoveAll = function (func) { obj.file.deleteMany({}, { multi: true }, func); };
|
||||
@ -1188,24 +1221,27 @@ module.exports.CreateDB = function (parent, func) {
|
||||
obj.StoreEvent = function (event, func) { // Fast MongoDB event store using bulkWrite()
|
||||
if (obj.eventsFilePendingSet == false) {
|
||||
// Perform the operation now
|
||||
obj.dbCounters.eventsSet++;
|
||||
obj.eventsFilePendingSet = true; obj.eventsFilePendingSets = null;
|
||||
if (func != null) { obj.eventsFilePendingCbs = [func]; }
|
||||
obj.eventsfile.bulkWrite([{ insertOne: { document: event } }], eventsFileBulkWriteCompleted);
|
||||
} else {
|
||||
// Add this operation to the pending list
|
||||
obj.dbCounters.eventsSetPending++;
|
||||
if (obj.eventsFilePendingSets == null) { obj.eventsFilePendingSets = [] }
|
||||
obj.eventsFilePendingSets.push(event);
|
||||
if (func != null) { if (obj.eventsFilePendingCb == null) { obj.eventsFilePendingCb = [func]; } else { obj.eventsFilePendingCb.push(func); } }
|
||||
}
|
||||
};
|
||||
} else {
|
||||
obj.StoreEvent = function (event, func) { obj.eventsfile.insertOne(event, func); };
|
||||
obj.StoreEvent = function (event, func) { obj.dbCounters.eventsSet++; obj.eventsfile.insertOne(event, func); };
|
||||
}
|
||||
|
||||
obj.GetEvents = function (ids, domain, func) { obj.eventsfile.find({ domain: domain, ids: { $in: ids } }).project({ type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).toArray(func); };
|
||||
obj.GetEventsWithLimit = function (ids, domain, limit, func) { obj.eventsfile.find({ domain: domain, ids: { $in: ids } }).project({ type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).limit(limit).toArray(func); };
|
||||
obj.GetUserEvents = function (ids, domain, username, func) { obj.eventsfile.find({ domain: domain, $or: [{ ids: { $in: ids } }, { username: username }] }).project({ type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).toArray(func); };
|
||||
obj.GetUserEventsWithLimit = function (ids, domain, username, limit, func) { obj.eventsfile.find({ domain: domain, $or: [{ ids: { $in: ids } }, { username: username }] }).project({ type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).limit(limit).toArray(func); };
|
||||
obj.GetUserLoginEvents = function (domain, username, func) { obj.eventsfile.find({ domain: domain, action: { $in: ['authfail', 'login'] }, msgArgs: { $exists: true } }).project({ action: 1, time: 1, msgid: 1, msgArgs: 1 }).sort({ time: -1 }).toArray(func); };
|
||||
obj.GetNodeEventsWithLimit = function (nodeid, domain, limit, func) { obj.eventsfile.find({ domain: domain, nodeid: nodeid }).project({ type: 0, etype: 0, _id: 0, domain: 0, ids: 0, node: 0, nodeid: 0 }).sort({ time: -1 }).limit(limit).toArray(func); };
|
||||
obj.GetNodeEventsSelfWithLimit = function (nodeid, domain, userid, limit, func) { obj.eventsfile.find({ domain: domain, nodeid: nodeid, userid: { $in: [userid, null] } }).project({ type: 0, etype: 0, _id: 0, domain: 0, ids: 0, node: 0, nodeid: 0 }).sort({ time: -1 }).limit(limit).toArray(func); };
|
||||
obj.RemoveAllEvents = function (domain) { obj.eventsfile.deleteMany({ domain: domain }, { multi: true }); };
|
||||
@ -1228,18 +1264,20 @@ module.exports.CreateDB = function (parent, func) {
|
||||
if (multiServer != null) { event.server = multiServer.serverid; }
|
||||
if (obj.powerFilePendingSet == false) {
|
||||
// Perform the operation now
|
||||
obj.dbCounters.powerSet++;
|
||||
obj.powerFilePendingSet = true; obj.powerFilePendingSets = null;
|
||||
if (func != null) { obj.powerFilePendingCbs = [func]; }
|
||||
obj.powerfile.bulkWrite([{ insertOne: { document: event } }], powerFileBulkWriteCompleted);
|
||||
} else {
|
||||
// Add this operation to the pending list
|
||||
obj.dbCounters.powerSetPending++;
|
||||
if (obj.powerFilePendingSets == null) { obj.powerFilePendingSets = [] }
|
||||
obj.powerFilePendingSets.push(event);
|
||||
if (func != null) { if (obj.powerFilePendingCb == null) { obj.powerFilePendingCb = [func]; } else { obj.powerFilePendingCb.push(func); } }
|
||||
}
|
||||
};
|
||||
} else {
|
||||
obj.storePowerEvent = function (event, multiServer, func) { if (multiServer != null) { event.server = multiServer.serverid; } obj.powerfile.insertOne(event, func); };
|
||||
obj.storePowerEvent = function (event, multiServer, func) { obj.dbCounters.powerSet++; if (multiServer != null) { event.server = multiServer.serverid; } obj.powerfile.insertOne(event, func); };
|
||||
}
|
||||
|
||||
obj.getPowerTimeline = function (nodeid, func) { obj.powerfile.find({ nodeid: { $in: ['*', nodeid] } }).project({ _id: 0, nodeid: 0, s: 0 }).sort({ time: 1 }).toArray(func); };
|
||||
@ -1302,7 +1340,11 @@ module.exports.CreateDB = function (parent, func) {
|
||||
|
||||
} else {
|
||||
// Database actions on the main collection (NeDB and MongoJS)
|
||||
obj.Set = function (data, func) { data = common.escapeLinksFieldNameEx(data); var xdata = performTypedRecordEncrypt(data); obj.file.update({ _id: xdata._id }, xdata, { upsert: true }, func); };
|
||||
obj.Set = function (data, func) {
|
||||
obj.dbCounters.fileSet++;
|
||||
data = common.escapeLinksFieldNameEx(data);
|
||||
var xdata = performTypedRecordEncrypt(data); obj.file.update({ _id: xdata._id }, xdata, { upsert: true }, func);
|
||||
};
|
||||
obj.Get = function (id, func) {
|
||||
if (arguments.length > 2) {
|
||||
var parms = [func];
|
||||
@ -1385,6 +1427,13 @@ module.exports.CreateDB = function (parent, func) {
|
||||
obj.eventsfile.find({ domain: domain, $or: [{ ids: { $in: ids } }, { username: username }] }, { type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).limit(limit, func);
|
||||
}
|
||||
};
|
||||
obj.GetUserLoginEvents = function (domain, username, func) {
|
||||
if (obj.databaseType == 1) {
|
||||
obj.eventsfile.find({ domain: domain, action: { $in: ['authfail', 'login'] }, msgArgs: { $exists: true } }, { action: 1, time: 1, msgid: 1, msgArgs: 1 }).sort({ time: -1 }).exec(func);
|
||||
} else {
|
||||
obj.eventsfile.find({ domain: domain, action: { $in: ['authfail', 'login'] }, msgArgs: { $exists: true } }, { action: 1, time: 1, msgid: 1, msgArgs: 1 }).sort({ time: -1 }, func);
|
||||
}
|
||||
};
|
||||
obj.GetNodeEventsWithLimit = function (nodeid, domain, limit, func) { if (obj.databaseType == 1) { obj.eventsfile.find({ domain: domain, nodeid: nodeid }, { type: 0, etype: 0, _id: 0, domain: 0, ids: 0, node: 0, nodeid: 0 }).sort({ time: -1 }).limit(limit).exec(func); } else { obj.eventsfile.find({ domain: domain, nodeid: nodeid }, { type: 0, etype: 0, _id: 0, domain: 0, ids: 0, node: 0, nodeid: 0 }).sort({ time: -1 }).limit(limit, func); } };
|
||||
obj.GetNodeEventsSelfWithLimit = function (nodeid, domain, userid, limit, func) { if (obj.databaseType == 1) { obj.eventsfile.find({ domain: domain, nodeid: nodeid, userid: { $in: [userid, null] } }, { type: 0, etype: 0, _id: 0, domain: 0, ids: 0, node: 0, nodeid: 0 }).sort({ time: -1 }).limit(limit).exec(func); } else { obj.eventsfile.find({ domain: domain, nodeid: nodeid }, { type: 0, etype: 0, _id: 0, domain: 0, ids: 0, node: 0, nodeid: 0 }).sort({ time: -1 }).limit(limit, func); } };
|
||||
obj.RemoveAllEvents = function (domain) { obj.eventsfile.remove({ domain: domain }, { multi: true }); };
|
||||
@ -1569,6 +1618,7 @@ module.exports.CreateDB = function (parent, func) {
|
||||
obj.filePendingRemoves = obj.filePendingRemove;
|
||||
obj.filePendingRemove = null;
|
||||
if (obj.filePendingRemoves != null) {
|
||||
obj.dbCounters.fileRemoveBulk++;
|
||||
var findlist = [], count = 0;
|
||||
for (var i in obj.filePendingRemoves) { findlist.push(i); count++; }
|
||||
obj.file.deleteMany({ _id: { $in: findlist } }, { multi: true }, fileBulkRemoveCompleted);
|
||||
@ -1584,6 +1634,7 @@ module.exports.CreateDB = function (parent, func) {
|
||||
}
|
||||
if (obj.filePendingSets != null) {
|
||||
// Perform pending operations
|
||||
obj.dbCounters.fileSetBulk++;
|
||||
var ops = [];
|
||||
obj.filePendingCbs = obj.filePendingCb;
|
||||
obj.filePendingCb = null;
|
||||
@ -1602,6 +1653,7 @@ module.exports.CreateDB = function (parent, func) {
|
||||
if (obj.eventsFilePendingCbs != null) { for (var i in obj.eventsFilePendingCbs) { obj.eventsFilePendingCbs[i](); } obj.eventsFilePendingCbs = null; }
|
||||
if (obj.eventsFilePendingSets != null) {
|
||||
// Perform pending operations
|
||||
obj.dbCounters.eventsSetBulk++;
|
||||
var ops = [];
|
||||
for (var i in obj.eventsFilePendingSets) { ops.push({ document: obj.eventsFilePendingSets[i] }); }
|
||||
obj.eventsFilePendingCbs = obj.eventsFilePendingCb;
|
||||
@ -1620,6 +1672,7 @@ module.exports.CreateDB = function (parent, func) {
|
||||
if (obj.powerFilePendingCbs != null) { for (var i in obj.powerFilePendingCbs) { obj.powerFilePendingCbs[i](); } obj.powerFilePendingCbs = null; }
|
||||
if (obj.powerFilePendingSets != null) {
|
||||
// Perform pending operations
|
||||
obj.dbCounters.powerSetBulk++;
|
||||
var ops = [];
|
||||
for (var i in obj.powerFilePendingSets) { ops.push({ document: obj.powerFilePendingSets[i] }); }
|
||||
obj.powerFilePendingCbs = obj.powerFilePendingCb;
|
||||
|
1
emails/translations/account-check-min_cs.html
Normal file
1
emails/translations/account-check-min_cs.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Ověření e-mailem</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Ověření</b></table><p>Ahoj [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> požaduje ověření e-mailem a dokončete proces kliknutím na následující odkaz.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Kliknutím sem ověřte svou e-mailovou adresu.</a></p>Pokud jste tento požadavek nezačali, ignorujte tento e-mail.</div>
|
1
emails/translations/account-check-min_de.html
Normal file
1
emails/translations/account-check-min_de.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - E-Mail-Überprüfung</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Überprüfung</b></table><p>Hallo [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> Wenn Sie eine E-Mail-Bestätigung anfordern, klicken Sie auf den folgenden Link, um den Vorgang abzuschließen.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Klicken Sie hier, um Ihre E-Mail-Adresse zu bestätigen.</a></p>Wenn Sie diese Anfrage nicht initiiert haben, ignorieren Sie diese Mail bitte.</div>
|
1
emails/translations/account-check-min_es.html
Normal file
1
emails/translations/account-check-min_es.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Verificación de Correo Electrónico</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Verificación</b></table><p>Hola [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> solicita la verificación por correo electrónico, haga clic en el siguiente enlace para completar el proceso.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Haga clic aquí para verificar su dirección de correo electrónico.</a></p>Si Ud. no inicio este requerimiento, por favor ignore este correo.</div>
|
1
emails/translations/account-check-min_fi.html
Normal file
1
emails/translations/account-check-min_fi.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Sähköpostivarmistus</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Todentaminen</b></table><p>Moi [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> pyytää sähköpostitse vahvistusta, suorita prosessi napsauttamalla seuraavaa linkkiä.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Napsauta tätä vahvistaaksesi sähköpostiosoitteesi.</a></p>Jos et suorittanut tätä pyyntöä, voit ohitaa tämän sähköpostin.</div>
|
1
emails/translations/account-check-min_fr.html
Normal file
1
emails/translations/account-check-min_fr.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Vérification E-mail</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Vérification</b></table><p>Bonjour [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> demande une vérification par e-mail, cliquez sur le lien suivant pour terminer le processus.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Cliquez ici pour vérifier votre adresse e-mail.</a></p>Si vous n'avez pas initié cette demande, veuillez ignorer ce courrier.</div>
|
1
emails/translations/account-check-min_hi.html
Normal file
1
emails/translations/account-check-min_hi.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - ईमेल सत्यापन</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - सत्यापन</b></table><p>हाय [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> ईमेल सत्यापन का अनुरोध कर रहा है, प्रक्रिया को पूरा करने के लिए निम्न लिंक पर क्लिक करें।<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">अपना ई-मेल पता सत्यापित करने के लिए यहां क्लिक करें।</a></p>यदि आपने यह अनुरोध आरंभ नहीं किया है, तो कृपया इस मेल को अनदेखा करें।</div>
|
1
emails/translations/account-check-min_ja.html
Normal file
1
emails/translations/account-check-min_ja.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - メールの確認</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - 確認</b></table><p>[[[USERNAME]]]様 <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> がメールの確認をリクエストしている場合は、次のリンクをクリックしてプロセスを完了してください。<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">ここをクリックして、電子メールアドレスを確認してください。</a></p>このリクエストを開始していない場合は、このメールを無視してください。</div>
|
1
emails/translations/account-check-min_ko.html
Normal file
1
emails/translations/account-check-min_ko.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Email 인증</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - 인증</b></table><p>안녕하세요, [[[USERNAME]]]님. <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> 이메일 검증을 요구하는 경우, 다음 링크를 클릭하여 과정을 완료하십시오.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">이메일 주소를 검증하려면 여기를 클릭하십시오.</a></p>이 요청을 시작하지 않은 경우, 이 메일을 무시하십시오.</div>
|
1
emails/translations/account-check-min_nl.html
Normal file
1
emails/translations/account-check-min_nl.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - E-mail Verificatie</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Verificatie</b></table><p>Hallo [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> vraagt om e-mailverificatie, klik op de volgende link om het proces te voltooien.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Klik hier om uw e-mailadres te verifiëren.</a></p>Als u dit verzoek niet heeft ingediend, dan kunt u deze e-mail negeren.</div>
|
1
emails/translations/account-check-min_pt.html
Normal file
1
emails/translations/account-check-min_pt.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Verificação de Email</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Verificação</b></table><p>Olá [[[NOME DE USUÁRIO]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> está solicitando verificação de e-mail, clique no link a seguir para concluir o processo.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Clique aqui para verificar seu endereço de e-mail.</a></p>Se você não iniciou esta solicitação, ignore este e-mail.</div>
|
1
emails/translations/account-check-min_ru.html
Normal file
1
emails/translations/account-check-min_ru.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - подтверждение по электронной почте</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Подтверждение</b></table><p>Привет [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> запрашивает подтверждение по электронной почте, нажмите на следующую ссылку, чтобы завершить процесс.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Нажмите здесь, чтобы подтвердить свой адрес электронной почты.</a></p>Если вы не инициировали этот запрос, игнорируйте это письмо.</div>
|
1
emails/translations/account-check-min_tr.html
Normal file
1
emails/translations/account-check-min_tr.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - E-posta Doğrulaması</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Doğrulama</b></table><p>Merhaba [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> e-posta doğrulaması istiyorsa, işlemi tamamlamak için aşağıdaki bağlantıya tıklayın.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">E-posta adresinizi doğrulamak için burayı tıklayın.</a></p>Bu isteği siz başlatmadıysanız, lütfen bu postayı dikkate almayın.</div>
|
1
emails/translations/account-check-min_zh-chs.html
Normal file
1
emails/translations/account-check-min_zh-chs.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]]-电邮验证</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]]-验证</b></table><p>[[[USERNAME]],你好, <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> 正在请求电邮验证,请单击以下连结以完成该过程。<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">单击此处以验证您的电邮地址。</a></p>如果您没有发起此请求,请不理此邮件。</div>
|
1
emails/translations/account-check-min_zh-cht.html
Normal file
1
emails/translations/account-check-min_zh-cht.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]]-電郵驗證</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]]-驗證</b></table><p>[[[USERNAME]],你好, <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]</a> 正在請求電郵驗證,請單擊以下鏈結以完成該過程。<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">單擊此處以驗證你的電郵地址。</a></p>如果你沒有發起此請求,請不理此電郵。</div>
|
1
emails/translations/account-invite-min_cs.html
Normal file
1
emails/translations/account-invite-min_cs.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Pozvánka na účet</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Pozvánka na účet</b></table><p>Účet byl pro vás vytvořen na serveru <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>, nyní k němu máte přístup:<p> Uživatelské jméno: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> Heslo: <b notrans=1>[[[PASSWORD]]]</b></p>S pozdravem,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/account-invite-min_de.html
Normal file
1
emails/translations/account-invite-min_de.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Kontoeinladung</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Kontoeinladung</b></table><p>Auf dem Server wurde ein Konto für Sie erstellt <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>können Sie jetzt darauf zugreifen mit:<p> Benutzername: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> Passwort: <b notrans=1>[[[PASSWORD]]]</b></p>Freundliche Grüße,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/account-invite-min_es.html
Normal file
1
emails/translations/account-invite-min_es.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Invitación de Cuenta</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Invitación de Cuenta</b></table><p>Una cuenta ha sido creada en su servidor <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>, ahora puede acceder con:<p> Nombre de usuario: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> Contraseña: <b notrans=1>[[[PASSWORD]]]</b></p>Atentamente,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/account-invite-min_fi.html
Normal file
1
emails/translations/account-invite-min_fi.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Tili kutsu</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Tili kutsu</b></table><p>Sinulle on luotu tili palvelimelle <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>, voit käyttää sitä nyt:<p> Käyttäjätunnus: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> Salasana: <b notrans=1>[[[PASSWORD]]]</b></p>Ystävällisin terveisin,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/account-invite-min_fr.html
Normal file
1
emails/translations/account-invite-min_fr.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Invitation au compte</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Invitation au compte</b></table><p>Un compte a été créé pour vous sur le serveur <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>, vous pouvez y accéder maintenant avec :<p> Nom d'utilisateur: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> Mot de passe: <b notrans=1>[[[PASSWORD]]]</b></p>Meilleures salutations,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/account-invite-min_hi.html
Normal file
1
emails/translations/account-invite-min_hi.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - खाता निमंत्रण</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - खाता निमंत्रण</b></table><p>सर्वर पर आपके लिए एक खाता बनाया गया था <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>, आप इसे अभी एक्सेस कर सकते हैं:<p> उपयोगकर्ता नाम: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> कुंजिका: <b notrans=1>[[[PASSWORD]]]</b></p>सादर,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/account-invite-min_ja.html
Normal file
1
emails/translations/account-invite-min_ja.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - アカウントの招待</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - アカウントの招待</b></table><p>サーバー上にアカウントが作成されました <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>、あなたは今それを使ってそれにアクセスできます:<p> ユーザー名: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> パスワード: <b notrans=1>[[[PASSWORD]]]</b></p>宜しくお願いします、<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/account-invite-min_ko.html
Normal file
1
emails/translations/account-invite-min_ko.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - 계정 초대</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - 계정 초대</b></table><p>당신을 위해 서버에서 한 계정이 생성되었습니다. <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>이제 당신은 다음으로 접근 가능합니다:<p> 사용자 이름: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> 암호: <b notrans=1>[[[PASSWORD]]]</b></p>최고의 안부를 전합니다,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/account-invite-min_nl.html
Normal file
1
emails/translations/account-invite-min_nl.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Account uitnodiging</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Account uitnodiging</b></table><p>Er is een account voor je aangemaakt op de server <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>, je hebt er nu toegang toe met:<p> Gebruikersnaam: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> Wachtwoord: <b notrans=1>[[[PASSWORD]]]</b></p>Vriendelijke groeten,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/account-invite-min_pt.html
Normal file
1
emails/translations/account-invite-min_pt.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Convite para conta</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Convite para conta</b></table><p>Uma conta foi criada para você no servidor <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>, você pode acessá-lo agora com:<p> Nome de usuário: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> Senha: <b notrans=1>[[[PASSWORD]]]</b></p>Cumprimentos,<br>[[[NOME DO USUÁRIO]]]<br></div>
|
1
emails/translations/account-invite-min_ru.html
Normal file
1
emails/translations/account-invite-min_ru.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - приглашение в аккаунт</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - приглашение в аккаунт</b></table><p>Учетная запись была создана для вас на сервере <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>Вы можете получить к нему доступ сейчас:<p> Имя пользователя: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> Пароль: <b notrans=1>[[[PASSWORD]]]</b></p>С уважением,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/account-invite-min_tr.html
Normal file
1
emails/translations/account-invite-min_tr.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Hesap Davetiyesi</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Hesap Davetiyesi</b></table><p>Sunucuda sizin için bir hesap oluşturuldu <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>, şimdi şununla erişebilirsiniz:<p> Kullanıcı adı: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> Parola: <b notrans=1>[[[PASSWORD]]]</b></p>Saygılarımla,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/account-invite-min_zh-chs.html
Normal file
1
emails/translations/account-invite-min_zh-chs.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]]-帐户邀请</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]]-帐户邀请</b></table><p>在服务器上为您创建了一个帐户 <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>,您现在可以通过以下方式访问它:<p> 用户名: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> 密码: <b notrans=1>[[[PASSWORD]]]</b></p>最好的祝福,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/account-invite-min_zh-cht.html
Normal file
1
emails/translations/account-invite-min_zh-cht.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]]-帳戶邀請</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]]-帳戶邀請</b></table><p>在伺服器上為你創建了一個帳戶 <a href=[[[SERVERURL]]][[[URLARGS1]]] notrans=1>[[[SERVERNAME]]]</a>,你現在可以通過以下方式訪問它:<p> 用戶名: <b notrans=1>[[[ACCOUNTNAME]]]</b><br> 密碼: <b notrans=1>[[[PASSWORD]]]</b></p>最好的祝福,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/account-login-min_cs.html
Normal file
1
emails/translations/account-login-min_cs.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Přihlášení k účtu</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Přihlášení k účtu</b></table><p>Váš přihlašovací token je: [[[TOKEN]]]<p>Tento token lze použít pouze jednou a je platný po dobu 5 minut.</div>
|
1
emails/translations/account-login-min_de.html
Normal file
1
emails/translations/account-login-min_de.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Konto-Login</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Konto-Login</b></table><p>Ihr Login-Token lautet: [[[TOKEN]]]<p>Dieser Token kann nur einmal verwendet werden und ist 5 Minuten gültig.</div>
|
1
emails/translations/account-login-min_es.html
Normal file
1
emails/translations/account-login-min_es.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Inicio de Sesión</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Inicio de Sesión</b></table><p>Su token de inicio de sesión es: [[[TOKEN]]]<p>Este token sólo se puede usar una vez y es válido durante 5 minutos.</div>
|
1
emails/translations/account-login-min_fi.html
Normal file
1
emails/translations/account-login-min_fi.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Tilille Kirjautuminen</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Tilille Kirjautuminen</b></table><p>Kirjautumistunnuksesi on: [[[TOKEN]]]<p>Tätä tunnusta voidaan käyttää vain kerran ja se on voimassa 5 minuuttia.</div>
|
1
emails/translations/account-login-min_fr.html
Normal file
1
emails/translations/account-login-min_fr.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Connexion au compte</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Connexion au compte</b></table><p>Votre jeton de connexion est: [[[TOKEN]]]<p>Ce jeton ne peut être utilisé qu'une seule fois et est valide pendant 5 minutes.</div>
|
1
emails/translations/account-login-min_hi.html
Normal file
1
emails/translations/account-login-min_hi.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - खाता लॉगिन</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - खाता लॉगिन</b></table><p>आपका लॉगिन टोकन है: [[[TOKEN]]]<p>यह टोकन केवल एक बार उपयोग किया जा सकता है और 5 मिनट के लिए वैध है।</div>
|
1
emails/translations/account-login-min_ja.html
Normal file
1
emails/translations/account-login-min_ja.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - アカウントログイン</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - アカウントログイン</b></table><p>ログイントークンは次のとおりです:[[[TOKEN]]]<p>このトークンは1回だけ使用でき、5分間有効です。</div>
|
1
emails/translations/account-login-min_ko.html
Normal file
1
emails/translations/account-login-min_ko.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - 계정 로그인</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - 계정 로그인</b></table><p>당신이 로그인한 토큰은 다음과 같습니다 : [[[TOKEN]]]<p>이 토큰은 오직 한 번만 사용될 수 있으며, 5분 동안만 유효합니다.</div>
|
1
emails/translations/account-login-min_nl.html
Normal file
1
emails/translations/account-login-min_nl.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Inloggen account</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Inloggen account</b></table><p>Uw login token is: [[[TOKEN]]]<p>Dit token kan maar één keer worden gebruikt en is 5 minuten geldig.</div>
|
1
emails/translations/account-login-min_pt.html
Normal file
1
emails/translations/account-login-min_pt.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Login da conta</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Login da conta</b></table><p>Seu token de login é: [[[TOKEN]]]<p>Este token só pode ser usado uma vez e é válido por 5 minutos.</div>
|
1
emails/translations/account-login-min_ru.html
Normal file
1
emails/translations/account-login-min_ru.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Вход в аккаунт</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Вход в аккаунт</b></table><p>Ваш токен для входа: [[[TOKEN]]]<p>Этот токен может быть использован только один раз и действителен в течение 5 минут.</div>
|
1
emails/translations/account-login-min_tr.html
Normal file
1
emails/translations/account-login-min_tr.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Hesap Girişi</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Hesap Girişi</b></table><p>Giriş jetonunuz: [[[TOKEN]]]<p>Bu simge yalnızca bir kez kullanılabilir ve 5 dakika geçerlidir.</div>
|
1
emails/translations/account-login-min_zh-chs.html
Normal file
1
emails/translations/account-login-min_zh-chs.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]]-帐户登录</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]]-帐户登录</b></table><p>您的登录保安编码为:[[[TOKEN]]]<p>此保安编码只能使用一次,有效期为5分钟。</div>
|
1
emails/translations/account-login-min_zh-cht.html
Normal file
1
emails/translations/account-login-min_zh-cht.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]]-帳戶登入</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]]-帳戶登入</b></table><p>你的登入保安編碼為:[[[TOKEN]]]<p>該保安編碼只能使用一次,有效期為5分鐘。</div>
|
1
emails/translations/account-reset-min_cs.html
Normal file
1
emails/translations/account-reset-min_cs.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Reset účtu</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Ověření</b></table><p>Ahoj [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> požaduje obnovení hesla k účtu, dokončete proces kliknutím na následující odkaz.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Kliknutím sem obnovíte heslo svého účtu.</a></p>Pokud jste tento požadavek nezačali, ignorujte tento e-mail.</div>
|
1
emails/translations/account-reset-min_de.html
Normal file
1
emails/translations/account-reset-min_de.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Zurücksetzen des Kontos</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Überprüfung</b></table><p>Hallo [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> Wenn Sie ein Zurücksetzen des Kontokennworts anfordern, klicken Sie auf den folgenden Link, um den Vorgang abzuschließen.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Klicken Sie hier, um Ihr Kontopasswort zurückzusetzen.</a></p>Wenn Sie diese Anfrage nicht initiiert haben, ignorieren Sie diese Mail bitte.</div>
|
1
emails/translations/account-reset-min_es.html
Normal file
1
emails/translations/account-reset-min_es.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Restablecimiento de Cuenta</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Verificación</b></table><p>Hola [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> solicita restablecer la contraseña de la cuenta, haga clic en el siguiente enlace para completar el proceso.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Haga clic aquí para restablecer la contraseña de su cuenta.</a></p>Si Ud. no inicio este requerimiento, por favor ignore este correo.</div>
|
1
emails/translations/account-reset-min_fi.html
Normal file
1
emails/translations/account-reset-min_fi.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Tili nollattu</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Todentaminen</b></table><p>Moi [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> pyytää tilin salasanan palauttamista, suorita prosessi napsauttamalla seuraavaa linkkiä.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Palauta tilisi salasana napsauttamalla tätä.</a></p>Jos et suorittanut tätä pyyntöä, voit ohitaa tämän sähköpostin.</div>
|
1
emails/translations/account-reset-min_fr.html
Normal file
1
emails/translations/account-reset-min_fr.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Réinitialisation du compte</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Vérification</b></table><p>Bonjour [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> demande une réinitialisation du mot de passe du compte, cliquez sur le lien suivant pour terminer le processus.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Cliquez ici pour réinitialiser le mot de passe de votre compte.</a></p>Si vous n'avez pas initié cette demande, veuillez ignorer ce courrier.</div>
|
1
emails/translations/account-reset-min_hi.html
Normal file
1
emails/translations/account-reset-min_hi.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - खाता रीसेट</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - सत्यापन</b></table><p>हाय [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> खाता पासवर्ड रीसेट का अनुरोध कर रहा है, प्रक्रिया पूरी करने के लिए निम्न लिंक पर क्लिक करें।<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">अपना खाता पासवर्ड रीसेट करने के लिए यहां क्लिक करें।</a></p>यदि आपने यह अनुरोध आरंभ नहीं किया है, तो कृपया इस मेल को अनदेखा करें।</div>
|
1
emails/translations/account-reset-min_ja.html
Normal file
1
emails/translations/account-reset-min_ja.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - アカウントのリセット</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - 確認</b></table><p>[[[USERNAME]]]様 <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> がアカウントパスワードのリセットを要求している場合は、次のリンクをクリックしてプロセスを完了します。<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">ここをクリックしてアカウントのパスワードをリセットしてください。</a></p>このリクエストを開始していない場合は、このメールを無視してください。</div>
|
1
emails/translations/account-reset-min_ko.html
Normal file
1
emails/translations/account-reset-min_ko.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - 계정 재설정</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - 인증</b></table><p>안녕하세요, [[[USERNAME]]]님. <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> 계정 비밀번호 재설정을 요청하는 경우, 다음 링크를 클릭하여 과정을 완료하십시오.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">당신의 계정 암호를 초기화하려면 여기를 클릭하십시오.</a></p>이 요청을 시작하지 않은 경우, 이 메일을 무시하십시오.</div>
|
1
emails/translations/account-reset-min_nl.html
Normal file
1
emails/translations/account-reset-min_nl.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Account Reset</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Verificatie</b></table><p>Hallo [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> vraagt om het opnieuw instellen van het wachtwoord van een account, klik op de volgende link om het proces te voltooien.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Klik hier om je wachtwoord opnieuw in te stellen.</a></p>Als u dit verzoek niet heeft ingediend, dan kunt u deze e-mail negeren.</div>
|
1
emails/translations/account-reset-min_pt.html
Normal file
1
emails/translations/account-reset-min_pt.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Redefinição de conta</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Verificação</b></table><p>Olá [[[NOME DE USUÁRIO]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> está solicitando a redefinição da senha da conta, clique no link a seguir para concluir o processo.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Clique aqui para redefinir a senha da sua conta.</a></p>Se você não iniciou esta solicitação, ignore este e-mail.</div>
|
1
emails/translations/account-reset-min_ru.html
Normal file
1
emails/translations/account-reset-min_ru.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Сброс учетной записи</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Подтверждение</b></table><p>Привет [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> запрашивает сброс пароля учетной записи, нажмите на следующую ссылку, чтобы завершить процесс.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Нажмите здесь, чтобы сбросить пароль учетной записи.</a></p>Если вы не инициировали этот запрос, игнорируйте это письмо.</div>
|
1
emails/translations/account-reset-min_tr.html
Normal file
1
emails/translations/account-reset-min_tr.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Hesabı Sıfırlama</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Doğrulama</b></table><p>Merhaba [[[USERNAME]]], <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> bir hesap şifresi sıfırlama talep ediyor, işlemi tamamlamak için aşağıdaki bağlantıya tıklayın.<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">Hesap şifrenizi sıfırlamak için burayı tıklayın.</a></p>Bu isteği siz başlatmadıysanız, lütfen bu postayı dikkate almayın.</div>
|
1
emails/translations/account-reset-min_zh-chs.html
Normal file
1
emails/translations/account-reset-min_zh-chs.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]]-帐户重置</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]]-验证</b></table><p>[[[USERNAME]],你好, <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> 正在要求重置帐户密码,请单击以下连结以完成该过程。<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">单击此处重置您的帐户密码。</a></p>如果您没有发起此请求,请不理此邮件。</div>
|
1
emails/translations/account-reset-min_zh-cht.html
Normal file
1
emails/translations/account-reset-min_zh-cht.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]]-帳戶重置</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]]-驗證</b></table><p>[[[USERNAME]],你好, <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]</a> 正在要求重置帳戶密碼,請單擊以下鏈結以完成該過程。<p style=margin-left:30px><a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]][[[URLARGS2]]]">單擊此處重置你的帳戶密碼。</a></p>如果你沒有發起此請求,請不理此電郵。</div>
|
1
emails/translations/mesh-invite-min_cs.html
Normal file
1
emails/translations/mesh-invite-min_cs.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Pozvánka</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Instalace agenta</b></table><area-name><p>Dobrý den, [[[NAME]]],</p></area-name><p>Uživatel [[[USERNAME]]] na serveru <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> vás žádá o instalaci softwaru pro spuštění relace dálkového ovládání.</p><area-msg><p>Zpráva: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Kliknutím sem stáhnete MeshAgent pro Windows.</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Kliknutím sem stáhnete MeshAgent pro Apple OSX.</a></p></area-osx><area-linux><p>V případě systému Linux vyjměte a vložte do terminálu a nainstalujte agenta:<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>Chcete-li nainstalovat software, <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>klikněte zde</a> a postupujte podle pokynů.</p></area-link><p>Pokud jste tento požadavek nezačali, ignorujte tento e-mail.</p>S pozdravem,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/mesh-invite-min_de.html
Normal file
1
emails/translations/mesh-invite-min_de.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Einladung</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Agenteninstallation</b></table><area-name><p>Hallo [[[NAME]]],</p></area-name><p>Benutzer [[[USERNAME]]] auf dem Server <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> fordert Sie auf, Software zu installieren, um eine Fernsteuerungssitzung zu starten.</p><area-msg><p>Botschaft: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Klicken Sie hier, um den MeshAgent für Windows herunterzuladen.</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Klicken Sie hier, um den MeshAgent für Apple OSX herunterzuladen.</a></p></area-osx><area-linux><p>Schneiden Sie unter Linux Folgendes aus und fügen Sie es in ein Terminal ein, um den Agenten zu installieren:<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>Um die Software zu installieren, <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>hier klicken</a> und folgen Sie den Anweisungen.</p></area-link><p>Wenn Sie diese Anfrage nicht initiiert haben, ignorieren Sie diese Mail bitte.</p>Freundliche Grüße,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/mesh-invite-min_es.html
Normal file
1
emails/translations/mesh-invite-min_es.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Invitación</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Instalación de Agente</b></table><area-name><p>Hola [[[NAME]]],</p></area-name><p>Usuario [[[USERNAME]]] en servidor <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> le solicita que instale software para iniciar una sesión de control remoto.</p><area-msg><p>Mensaje: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Haga clic aquí para descargar el Agente Mesh para Windows.</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Haga clic aquí para descargar el Agente Mesh para Apple OSX.</a></p></area-osx><area-linux><p>Para Linux, copie y pegue lo siguiente en la terminal para instalar el agente:<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>Para instalar el software, <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>haga clic aquí</a> y siga las instrucciones.</p></area-link><p>Si Ud. no inicio este requerimiento, por favor ignore este correo.</p>Atentamente,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/mesh-invite-min_fi.html
Normal file
1
emails/translations/mesh-invite-min_fi.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Kutsu</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Agentin asennus</b></table><area-name><p>Hei [[[NAME]]],</p></area-name><p>Käyttäjä [[[USERNAME]]] palvelimella <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> pyytää sinua asentamaan ohjelmiston etäohjausistunnon aloittamiseksi.</p><area-msg><p>Viesti: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Napsauta tätä ladataksesi MeshAgent Windows:lle.</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Napsauta tätä ladataksesi MeshAgent Apple OSX:lle.</a></p></area-osx><area-linux><p>Linux: leikkaa ja liitä seuraava päätelaitteeseen agentin asentamiseksi:<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>Asentaaksesi ohjelman, <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>klikkaa tästä</a> ja seuraa ohjeita.</p></area-link><p>Jos et suorittanut tätä pyyntöä, voit ohitaa tämän sähköpostin.</p>Ystävällisin terveisin,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/mesh-invite-min_fr.html
Normal file
1
emails/translations/mesh-invite-min_fr.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Invitation</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Installation de l'agent</b></table><area-name><p>Bonjour [[[NAME]]],</p></area-name><p>Utilisateur [[[USERNAME]]] sur le serveur <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> vous demande d'installer un logiciel pour démarrer une session de contrôle à distance.</p><area-msg><p>Message: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Cliquez ici pour télécharger le MeshAgent pour Windows.</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Cliquez ici pour télécharger le MeshAgent pour Apple OSX.</a></p></area-osx><area-linux><p>Pour Linux, copiez et collez les éléments suivants dans un terminal pour installer l'agent:<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>Pour installer le logiciel, <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>cliquez ici</a> et suivez les instructions.</p></area-link><p>Si vous n'avez pas initié cette demande, veuillez ignorer ce courrier.</p>Meilleures salutations,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/mesh-invite-min_hi.html
Normal file
1
emails/translations/mesh-invite-min_hi.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - निमंत्रण</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - एजेंट स्थापना</b></table><area-name><p>नमस्कार [[[NAME]]],</p></area-name><p>उपयोगकर्ता [[[USERNAME]]] सर्वर पर <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> रिमोट कंट्रोल सेशन शुरू करने के लिए आपको सॉफ्टवेयर इंस्टॉल करने का अनुरोध कर रहा है।</p><area-msg><p>संदेश: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">विंडोज के लिए मेशजेंट को डाउनलोड करने के लिए यहां क्लिक करें।</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Apple OSX के लिए मेशजेंट को डाउनलोड करने के लिए यहां क्लिक करें।</a></p></area-osx><area-linux><p>लिनक्स में, एजेंट को स्थापित करने के लिए टर्मिनल में निम्नलिखित को काटें और चिपकाएँ:<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>सॉफ़्टवेयर स्थापित करने के लिए, <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>यहाँ क्लिक करें</a> और निर्देशों का पालन करें।</p></area-link><p>यदि आपने यह अनुरोध आरंभ नहीं किया है, तो कृपया इस मेल को अनदेखा करें।</p>सादर,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/mesh-invite-min_ja.html
Normal file
1
emails/translations/mesh-invite-min_ja.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - 招待</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - エージェントのインストール</b></table><area-name><p>[[[NAME]]]様</p></area-name><p>サーバー上のユーザー[[[USERNAME]]] <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> リモートコントロールセッションを開始するソフトウェアをインストールするように要求しています。</p><area-msg><p>メッセージ: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Windows用のMeshAgentをダウンロードするには、ここをクリックしてください。</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">ここをクリックして、Apple OSX用のMeshAgentをダウンロードしてください。</a></p></area-osx><area-linux><p>Linuxの場合は、ターミナルで以下をカットアンドペーストしてエージェントをインストールします。<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>ソフトウェアをインストールするには、 <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>ここをクリック</a> 指示に従ってください。</p></area-link><p>このリクエストを開始していない場合は、このメールを無視してください。</p>宜しくお願いします、<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/mesh-invite-min_ko.html
Normal file
1
emails/translations/mesh-invite-min_ko.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - 초대</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - 에이전트 설치</b></table><area-name><p>안녕하세요, [[[NAME]]]님.</p></area-name><p>서버의 [[[USERNAME]]] 사용자 <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> 원격 제어 세션을 시작하기 위해 소프트웨어 설치를 요청하고 있습니다.</p><area-msg><p>메시지: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Windows를 위한 MeshAgent를 다운로드 하려면 여기를 클릭하십시오.</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Apple OSX를 위한 MeshAgent를 다운로드 하려면 여기를 클릭하십시오.</a></p></area-osx><area-linux><p>Linux의 경우, 다음을 잘라내어 터미널에 붙여 넣어 에이전트를 설치하십시오.<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>소프트웨어를 설치하려면, <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>여기를 클릭하십시오.</a> 그리고 다음 지시들을 따르십시오.</p></area-link><p>이 요청을 시작하지 않은 경우, 이 메일을 무시하십시오.</p>최고의 안부를 전합니다,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/mesh-invite-min_nl.html
Normal file
1
emails/translations/mesh-invite-min_nl.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Uitnodiging</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Agent installatie</b></table><area-name><p>Hallo [[[NAME]]],</p></area-name><p>Gebruiker [[[USERNAME]]] op server <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> vraagt u om de software te installeren om een ondersteunings sessie te starten.</p><area-msg><p>Bericht: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Klik hier om de MeshAgent te downloaden voor Windows.</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Klik hier om de MeshAgent te downloaden voor Apple OSX.</a></p></area-osx><area-linux><p>Voor Linux, knip het volgende en plak dit in een terminal om de agent te installeren:<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>Om de software te installeren, <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>Klik hier</a> en volg de instructies.</p></area-link><p>Als u dit verzoek niet heeft ingediend, dan kunt u deze e-mail negeren.</p>Vriendelijke groeten,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/mesh-invite-min_pt.html
Normal file
1
emails/translations/mesh-invite-min_pt.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Convite</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Instalação do Agente</b></table><area-name><p>Olá [[[NAME]]],</p></area-name><p>Usuário [[[USERNAME]]] no servidor <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> está solicitando a instalação de um software para iniciar uma sessão de controle remoto.</p><area-msg><p>Mensagem: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Clique aqui para baixar o MeshAgent para Windows.</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Clique aqui para baixar o MeshAgent para Apple OSX.</a></p></area-osx><area-linux><p>Para Linux, recorte e cole o seguinte em um terminal para instalar o agente:<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>Para instalar o software, <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>Clique aqui</a> e siga as instruções.</p></area-link><p>Se você não iniciou esta solicitação, ignore este e-mail.</p>Cumprimentos,<br>[[[NOME DO USUÁRIO]]]<br></div>
|
1
emails/translations/mesh-invite-min_ru.html
Normal file
1
emails/translations/mesh-invite-min_ru.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Приглашение</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Установка агента</b></table><area-name><p>Здравствуйте, [[[NAME]]],</p></area-name><p>Пользователь [[[USERNAME]]] на сервере <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> просит вас установить программное обеспечение, чтобы начать сеанс удаленного управления.</p><area-msg><p>Сообщение: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Нажмите здесь, чтобы загрузить MeshAgent для Windows.</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Нажмите здесь, чтобы загрузить MeshAgent для Apple OSX.</a></p></area-osx><area-linux><p>Для Linux вырезайте и вставляйте в терминал следующее, чтобы установить агент:<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>Чтобы установить программное обеспечение, <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>кликните сюда</a> и следуйте инструкциям.</p></area-link><p>Если вы не инициировали этот запрос, игнорируйте это письмо.</p>С уважением,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/mesh-invite-min_tr.html
Normal file
1
emails/translations/mesh-invite-min_tr.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]] - Davet</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]] - Aracı Kurulumu</b></table><area-name><p>Merhaba [[[NAME]]],</p></area-name><p>Sunucudaki kullanıcı [[[USERNAME]]] <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> bir uzaktan kontrol oturumu başlatmak için yazılım yüklemenizi istiyor.</p><area-msg><p>İleti: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Windows için MeshAgent'ı indirmek için buraya tıklayın.</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Apple OSX için MeshAgent'ı indirmek için burayı tıklayın.</a></p></area-osx><area-linux><p>Linux için, aracıyı yüklemek için aşağıdakileri kesip bir terminale yapıştırın:<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>Yazılımı kurmak için, <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>buraya Tıkla</a> ve talimatları izleyin.</p></area-link><p>Bu isteği siz başlatmadıysanız, lütfen bu postayı dikkate almayın.</p>Saygılarımla,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/mesh-invite-min_zh-chs.html
Normal file
1
emails/translations/mesh-invite-min_zh-chs.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]]-邀请</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]]-代理程序安装</b></table><area-name><p>您好[[[NAME]]],</p></area-name><p>服务器上的使用者[[[USERNAME]]] <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]]</a> 正在要求您安装软件以启动远程控制会话。</p><area-msg><p>消息: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">单击此处下载适用于Windows的MeshAgent。</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">单击此处下载适用于Apple OSX的MeshAgent。</a></p></area-osx><area-linux><p>对于Linux,将以下内容剪切并粘贴到终端中以安装代理软件:<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>要安装软件, <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>点击这里</a> 并按照说明进行操作。</p></area-link><p>如果您没有发起此请求,请不理此邮件。</p>最好的祝福,<br>[[[USERNAME]]]<br></div>
|
1
emails/translations/mesh-invite-min_zh-cht.html
Normal file
1
emails/translations/mesh-invite-min_zh-cht.html
Normal file
@ -0,0 +1 @@
|
||||
<div>[[[SERVERNAME]]]-邀請</div><div style=font-family:Arial,Helvetica,sans-serif><table style=background-color:#036;color:#d3d3d3;width:100% cellpadding=8><tr><td><b style=font-size:20px;font-family:Arial,Helvetica,sans-serif>[[[SERVERNAME]]]-代理程序安裝</b></table><area-name><p>你好[[[NAME]]],</p></area-name><p>伺服器上的使用者[[[USERNAME]]] <a href=[[[SERVERURL]]][[[URLARGS1]]]>[[[SERVERNAME]]</a> 正在要求你安裝軟體以啟動遠程控制會話。</p><area-msg><p>訊息: <b notrans=1>[[[MSG]]]</b></p></area-msg><area-windows><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshagents?id=4&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">單擊此處下載適用於Windows的MeshAgent。</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">單擊此處下載適用於Apple OSX的MeshAgent。</a></p></area-osx><area-linux><p>對於Linux,將以下內容剪切並粘貼到終端中以安裝代理軟體:<br><pre style=margin-left:30px notrans=1>wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre><p></p></area-linux><area-link><p>要安裝軟體, <a href=[[[SERVERURL]]][[[LINKURL]]][[[URLARGS2]]]>點擊這裡</a> 並按照說明進行操作。</p></area-link><p>如果你沒有發起此請求,請不理此電郵。</p>最好的祝福,<br>[[[USERNAME]]]<br></div>
|
58
firebase.js
58
firebase.js
@ -35,9 +35,19 @@ module.exports.CreateFirebase = function (parent, senderid, serverkey) {
|
||||
|
||||
var tokenToNodeMap = {} // Token --> { nid: nodeid, mid: meshid }
|
||||
|
||||
// Setup logging
|
||||
if (parent.config.firebase && (parent.config.firebase.log === true)) {
|
||||
obj.logpath = parent.path.join(parent.datapath, 'firebase.txt');
|
||||
obj.log = function (msg) { try { parent.fs.appendFileSync(obj.logpath, new Date().toLocaleString() + ': ' + msg + '\r\n'); } catch (ex) { console.log('ERROR: Unable to write to firebase.txt.'); } }
|
||||
} else {
|
||||
obj.log = function () { }
|
||||
}
|
||||
|
||||
// Messages received from client (excluding receipts)
|
||||
xcs.on('message', function (messageId, from, data, category) {
|
||||
parent.debug('email', 'Firebase-Message: ' + JSON.stringify(data));
|
||||
const jsonData = JSON.stringify(data);
|
||||
obj.log('Firebase-Message: ' + jsonData);
|
||||
parent.debug('email', 'Firebase-Message: ' + jsonData);
|
||||
|
||||
if (typeof data.r == 'string') {
|
||||
// Lookup push relay server
|
||||
@ -73,6 +83,7 @@ module.exports.CreateFirebase = function (parent, senderid, serverkey) {
|
||||
|
||||
xcs.start();
|
||||
|
||||
obj.log('CreateFirebase-Setup');
|
||||
parent.debug('email', 'CreateFirebase-Setup');
|
||||
|
||||
// EXAMPLE
|
||||
@ -83,6 +94,7 @@ module.exports.CreateFirebase = function (parent, senderid, serverkey) {
|
||||
obj.sendToDevice = function (node, payload, options, func) {
|
||||
parent.debug('email', 'Firebase-sendToDevice');
|
||||
if ((node == null) || (typeof node.pmt != 'string')) return;
|
||||
obj.log('sendToDevice, node:' + node._id + ', payload: ' + JSON.stringify(payload) + ', options: ' + JSON.stringify(options));
|
||||
|
||||
// Fill in our lookup table
|
||||
if (node._id != null) { tokenToNodeMap[node.pmt] = { nid: node._id, mid: node.meshid, did: node.domain } }
|
||||
@ -106,7 +118,7 @@ module.exports.CreateFirebase = function (parent, senderid, serverkey) {
|
||||
|
||||
// Send the message
|
||||
function callback(result) {
|
||||
if (result.getError() == null) { obj.stats.sent++; } else { obj.stats.sendError++; }
|
||||
if (result.getError() == null) { obj.stats.sent++; obj.log('Success'); } else { obj.stats.sendError++; obj.log('Fail'); }
|
||||
callback.func(result.getMessageId(), result.getError(), result.getErrorDescription())
|
||||
}
|
||||
callback.func = func;
|
||||
@ -125,11 +137,13 @@ module.exports.CreateFirebase = function (parent, senderid, serverkey) {
|
||||
ws.on('message', function (msg) {
|
||||
parent.debug('email', 'FBWS-Data(' + this.relayId + '): ' + msg);
|
||||
if (typeof msg == 'string') {
|
||||
obj.log('Relay: ' + msg);
|
||||
|
||||
// Parse the incoming push request
|
||||
var data = null;
|
||||
try { data = JSON.parse(msg) } catch (ex) { return; }
|
||||
if (typeof data != 'object') return;
|
||||
if (parent.common.validateObjectForMongo(data, 4096) == false) return; // Perform sanity checking on this object.
|
||||
if (typeof data.pmt != 'string') return;
|
||||
if (typeof data.payload != 'object') return;
|
||||
if (typeof data.payload.notification == 'object') {
|
||||
@ -191,19 +205,32 @@ module.exports.CreateFirebaseRelay = function (parent, url, key) {
|
||||
const relayUrl = require('url').parse(url);
|
||||
parent.debug('email', 'CreateFirebaseRelay-Setup');
|
||||
|
||||
// Setup logging
|
||||
if (parent.config.firebaserelay && (parent.config.firebaserelay.log === true)) {
|
||||
obj.logpath = parent.path.join(parent.datapath, 'firebaserelay.txt');
|
||||
obj.log = function (msg) { try { parent.fs.appendFileSync(obj.logpath, new Date().toLocaleString() + ': ' + msg + '\r\n'); } catch (ex) { console.log('ERROR: Unable to write to firebaserelay.txt.'); } }
|
||||
} else {
|
||||
obj.log = function () { }
|
||||
}
|
||||
|
||||
obj.log('Starting relay to: ' + relayUrl.href);
|
||||
if (relayUrl.protocol == 'wss:') {
|
||||
// Setup two-way push notification channel
|
||||
obj.wsopen = false;
|
||||
obj.tokenToNodeMap = {} // Token --> { nid: nodeid, mid: meshid }
|
||||
obj.tokenToNodeMap = {}; // Token --> { nid: nodeid, mid: meshid }
|
||||
obj.backoffTimer = 0;
|
||||
obj.connectWebSocket = function () {
|
||||
if (obj.reconnectTimer != null) { try { clearTimeout(obj.reconnectTimer); } catch (ex) { } delete obj.reconnectTimer; }
|
||||
if (obj.wsclient != null) return;
|
||||
obj.wsclient = new WebSocket(relayUrl.href + (key ? ('?key=' + key) : ''), { rejectUnauthorized: false })
|
||||
obj.wsclient.on('open', function () {
|
||||
obj.lastConnect = Date.now();
|
||||
parent.debug('email', 'FBWS-Connected');
|
||||
obj.wsopen = true;
|
||||
});
|
||||
obj.wsclient.on('message', function (msg) {
|
||||
parent.debug('email', 'FBWS-Data(' + msg.length + '): ' + msg);
|
||||
obj.log('Received(' + msg.length + '): ' + msg);
|
||||
var data = null;
|
||||
try { data = JSON.parse(msg) } catch (ex) { }
|
||||
if (typeof data != 'object') return;
|
||||
@ -212,16 +239,20 @@ module.exports.CreateFirebaseRelay = function (parent, url, key) {
|
||||
if (typeof data.category != 'string') return;
|
||||
processMessage(data.messageId, data.from, data.data, data.category);
|
||||
});
|
||||
obj.wsclient.on('error', function (err) {
|
||||
obj.wsclient = null;
|
||||
obj.wsopen = false;
|
||||
setTimeout(obj.connectWebSocket, 2000);
|
||||
});
|
||||
obj.wsclient.on('close', function () {
|
||||
obj.wsclient.on('error', function (err) { obj.log('Error: ' + err); });
|
||||
obj.wsclient.on('close', function (a, b, c) {
|
||||
parent.debug('email', 'FBWS-Disconnected');
|
||||
obj.wsclient = null;
|
||||
obj.wsopen = false;
|
||||
setTimeout(obj.connectWebSocket, 2000);
|
||||
|
||||
// Compute the backoff timer
|
||||
if (obj.reconnectTimer == null) {
|
||||
if ((obj.lastConnect != null) && ((Date.now() - obj.lastConnect) > 10000)) { obj.backoffTimer = 0; }
|
||||
obj.backoffTimer += 1000;
|
||||
obj.backoffTimer = obj.backoffTimer * 2;
|
||||
if (obj.backoffTimer > 1200000) { obj.backoffTimer = 600000; } // Maximum 10 minutes backoff.
|
||||
obj.reconnectTimer = setTimeout(obj.connectWebSocket, obj.backoffTimer);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -241,6 +272,7 @@ module.exports.CreateFirebaseRelay = function (parent, url, key) {
|
||||
obj.sendToDevice = function (node, payload, options, func) {
|
||||
parent.debug('email', 'Firebase-sendToDevice-webSocket');
|
||||
if ((node == null) || (typeof node.pmt != 'string')) { func(0, 'error'); return; }
|
||||
obj.log('sendToDevice, node:' + node._id + ', payload: ' + JSON.stringify(payload) + ', options: ' + JSON.stringify(options));
|
||||
|
||||
// Fill in our lookup table
|
||||
if (node._id != null) { obj.tokenToNodeMap[node.pmt] = { nid: node._id, mid: node.meshid, did: node.domain } }
|
||||
@ -253,11 +285,13 @@ module.exports.CreateFirebaseRelay = function (parent, url, key) {
|
||||
if (obj.wsopen == true) {
|
||||
try { obj.wsclient.send(JSON.stringify({ pmt: node.pmt, payload: payload, options: options })); } catch (ex) { func(0, 'error'); obj.stats.sendError++; return; }
|
||||
obj.stats.sent++;
|
||||
obj.log('Sent');
|
||||
func(1);
|
||||
} else {
|
||||
// TODO: Buffer the push messages until TTL.
|
||||
func(0, 'error');
|
||||
obj.stats.sendError++;
|
||||
obj.log('Error');
|
||||
func(0, 'error');
|
||||
}
|
||||
}
|
||||
obj.connectWebSocket();
|
||||
@ -268,6 +302,7 @@ module.exports.CreateFirebaseRelay = function (parent, url, key) {
|
||||
parent.debug('email', 'Firebase-sendToDevice-httpPost');
|
||||
if ((node == null) || (typeof node.pmt != 'string')) return;
|
||||
|
||||
obj.log('sendToDevice, node:' + node._id + ', payload: ' + JSON.stringify(payload) + ', options: ' + JSON.stringify(options));
|
||||
const querydata = querystring.stringify({ 'msg': JSON.stringify({ pmt: node.pmt, payload: payload, options: options }) });
|
||||
|
||||
// Fill in the server agent cert hash
|
||||
@ -287,6 +322,7 @@ module.exports.CreateFirebaseRelay = function (parent, url, key) {
|
||||
}
|
||||
}
|
||||
const req = https.request(httpOptions, function (res) {
|
||||
obj.log('Response: ' + res.statusCode);
|
||||
if (res.statusCode == 200) { obj.stats.sent++; } else { obj.stats.sendError++; }
|
||||
if (func != null) { func(++obj.messageId, (res.statusCode == 200) ? null : 'error'); }
|
||||
});
|
||||
|
@ -1170,6 +1170,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||
obj.agentCoreUpdateTaskId = taskid;
|
||||
const url = '*' + require('url').parse(obj.agentExeInfo.url).path;
|
||||
var cmd = { action: 'agentupdate', url: url, hash: obj.agentExeInfo.hashhex };
|
||||
parent.parent.debug('agentupdate', "Sending agent update url: " + cmd.url);
|
||||
|
||||
// Add the hash
|
||||
if (obj.agentExeInfo.fileHash != null) { cmd.hash = obj.agentExeInfo.fileHashHex; } else { cmd.hash = obj.agentExeInfo.hashhex; }
|
||||
@ -1360,6 +1361,10 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||
}
|
||||
case 'sysinfo': {
|
||||
if ((typeof command.data == 'object') && (typeof command.data.hash == 'string')) {
|
||||
// Validate command.data.
|
||||
if (common.validateObjectForMongo(command.data, 1024) == false) break;
|
||||
|
||||
// Save to database
|
||||
command.data._id = 'si' + obj.dbNodeKey;
|
||||
command.data.type = 'sysinfo';
|
||||
command.data.domain = domain.id;
|
||||
@ -1482,6 +1487,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
||||
obj.agentCoreUpdateTaskId = taskid;
|
||||
const url = '*' + require('url').parse(obj.agentExeInfo.url).path;
|
||||
var cmd = { action: 'agentupdate', url: url, hash: obj.agentExeInfo.hashhex, sessionid: agentUpdateFunc.sessionid };
|
||||
parent.parent.debug('agentupdate', "Sending user requested agent update url: " + cmd.url);
|
||||
|
||||
// Add the hash
|
||||
if (obj.agentExeInfo.fileHash != null) { cmd.hash = obj.agentExeInfo.fileHashHex; } else { cmd.hash = obj.agentExeInfo.hashhex; }
|
||||
|
@ -248,6 +248,10 @@
|
||||
"description": "The port on the remote device.",
|
||||
"type": "number"
|
||||
},
|
||||
"ip": {
|
||||
"description": "Target IP address. If not specified, the target of the connection is the remote device running the MeshAgent.",
|
||||
"type": "string"
|
||||
},
|
||||
"filter": {
|
||||
"description": "Array of node/domain/id or mesh/domain/id strings. When set, the link will only show up for the specified devices or device groups.",
|
||||
"type": "array",
|
||||
|
@ -454,7 +454,7 @@ function CreateMeshCentralServer(config, args) {
|
||||
if (error != null) {
|
||||
// This is an un-expected restart
|
||||
console.log(error);
|
||||
console.log('ERROR: MeshCentral failed with critical error, check MeshErrors.txt. Restarting in 5 seconds...');
|
||||
console.log('ERROR: MeshCentral failed with critical error, check mesherrors.txt. Restarting in 5 seconds...');
|
||||
setTimeout(function () { obj.launchChildServer(startArgs); }, 5000);
|
||||
}
|
||||
}
|
||||
|
56
meshuser.js
56
meshuser.js
@ -887,7 +887,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
|
||||
switch (cmd) {
|
||||
case 'help': {
|
||||
var fin = '', f = '', availcommands = 'help,maintenance,info,versions,resetserver,usersessions,closeusersessions,tasklimiter,setmaxtasks,cores,migrationagents,agentstats,agentissues,webstats,mpsstats,swarmstats,acceleratorsstats,updatecheck,serverupdate,nodeconfig,heapdump,relays,autobackup,backupconfig,dupagents,dispatchtable,badlogins,showpaths,le,lecheck,leevents,dbstats,sms,amtacm,certhashes,watchdog,amtmanager';
|
||||
var fin = '', f = '', availcommands = 'help,maintenance,info,versions,resetserver,usersessions,closeusersessions,tasklimiter,setmaxtasks,cores,migrationagents,agentstats,agentissues,webstats,mpsstats,swarmstats,acceleratorsstats,updatecheck,serverupdate,nodeconfig,heapdump,relays,autobackup,backupconfig,dupagents,dispatchtable,badlogins,showpaths,le,lecheck,leevents,dbstats,dbcounters,sms,amtacm,certhashes,watchdog,amtmanager';
|
||||
if (parent.parent.config.settings.heapdump === true) { availcommands += ',heapdump'; }
|
||||
availcommands = availcommands.split(',').sort();
|
||||
while (availcommands.length > 0) { if (f.length > 80) { fin += (f + ',\r\n'); f = ''; } f += (((f != '') ? ', ' : ' ') + availcommands.shift()); }
|
||||
@ -1135,7 +1135,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
var r2 = '';
|
||||
for (var i in stats) { r2 += (i + ': ' + stats[i] + '\r\n'); }
|
||||
try { ws.send(JSON.stringify({ action: 'serverconsole', value: r2, tag: command.tag })); } catch (ex) { }
|
||||
})
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'dbcounters': {
|
||||
try { ws.send(JSON.stringify({ action: 'serverconsole', value: JSON.stringify(parent.parent.db.dbCounters, null, 2), tag: command.tag })); } catch (ex) { }
|
||||
break;
|
||||
}
|
||||
case 'serverupdate': {
|
||||
@ -4156,6 +4160,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
if (command.nodeid) { cookieContent.nodeid = command.nodeid; }
|
||||
if (command.tcpaddr) { cookieContent.tcpaddr = command.tcpaddr; } // Indicates the browser want to agent to TCP connect to a remote address
|
||||
if (command.tcpport) { cookieContent.tcpport = command.tcpport; } // Indicates the browser want to agent to TCP connect to a remote port
|
||||
if (command.ip) { cookieContent.ip = command.ip; } // Indicates the browser want to agent to relay a TCP connection to a IP:port
|
||||
command.cookie = parent.parent.encodeCookie(cookieContent, parent.parent.loginCookieEncryptionKey);
|
||||
command.trustedCert = parent.isTrustedCert(domain);
|
||||
try { ws.send(JSON.stringify(command)); } catch (ex) { }
|
||||
@ -5387,23 +5392,46 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
// TODO: Make a better database call to get filtered data.
|
||||
if (command.userid == null) {
|
||||
// Get previous logins for self
|
||||
db.GetUserEvents([user._id], domain.id, user._id.split('/')[2], function (err, docs) {
|
||||
if (err != null) return;
|
||||
var e = [];
|
||||
for (var i in docs) { if ((docs[i].msgArgs) && ((docs[i].action == 'authfail') || (docs[i].action == 'login'))) { e.push({ t: docs[i].time, m: docs[i].msgid, a: docs[i].msgArgs }); } }
|
||||
try { ws.send(JSON.stringify({ action: 'previousLogins', events: e })); } catch (ex) { }
|
||||
});
|
||||
if (db.GetUserLoginEvents) {
|
||||
// New way
|
||||
db.GetUserLoginEvents(domain.id, user._id.split('/')[2], function (err, docs) {
|
||||
if (err != null) return;
|
||||
var e = [];
|
||||
for (var i in docs) { e.push({ t: docs[i].time, m: docs[i].msgid, a: docs[i].msgArgs }); }
|
||||
try { ws.send(JSON.stringify({ action: 'previousLogins', events: e })); } catch (ex) { }
|
||||
});
|
||||
} else {
|
||||
// Old way
|
||||
db.GetUserEvents([user._id], domain.id, user._id.split('/')[2], function (err, docs) {
|
||||
console.log(docs);
|
||||
if (err != null) return;
|
||||
var e = [];
|
||||
for (var i in docs) { if ((docs[i].msgArgs) && ((docs[i].action == 'authfail') || (docs[i].action == 'login'))) { e.push({ t: docs[i].time, m: docs[i].msgid, a: docs[i].msgArgs }); } }
|
||||
try { ws.send(JSON.stringify({ action: 'previousLogins', events: e })); } catch (ex) { }
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Get previous logins for specific userid
|
||||
if (user.siteadmin === SITERIGHT_ADMIN) {
|
||||
var splitUser = command.userid.split('/');
|
||||
if ((obj.crossDomain === true) || (splitUser[1] === domain.id)) {
|
||||
db.GetUserEvents([command.userid], splitUser[1], splitUser[2], function (err, docs) {
|
||||
if (err != null) return;
|
||||
var e = [];
|
||||
for (var i in docs) { if ((docs[i].msgArgs) && ((docs[i].action == 'authfail') || (docs[i].action == 'login'))) { e.push({ t: docs[i].time, m: docs[i].msgid, a: docs[i].msgArgs }); } }
|
||||
try { ws.send(JSON.stringify({ action: 'previousLogins', userid: command.userid, events: e })); } catch (ex) { }
|
||||
});
|
||||
if (db.GetUserLoginEvents) {
|
||||
// New way
|
||||
db.GetUserLoginEvents(splitUser[1], splitUser[2], function (err, docs) {
|
||||
if (err != null) return;
|
||||
var e = [];
|
||||
for (var i in docs) { e.push({ t: docs[i].time, m: docs[i].msgid, a: docs[i].msgArgs }); }
|
||||
try { ws.send(JSON.stringify({ action: 'previousLogins', userid: command.userid, events: e })); } catch (ex) { }
|
||||
});
|
||||
} else {
|
||||
// Old way
|
||||
db.GetUserEvents([command.userid], splitUser[1], splitUser[2], function (err, docs) {
|
||||
if (err != null) return;
|
||||
var e = [];
|
||||
for (var i in docs) { if ((docs[i].msgArgs) && ((docs[i].action == 'authfail') || (docs[i].action == 'login'))) { e.push({ t: docs[i].time, m: docs[i].msgid, a: docs[i].msgArgs }); } }
|
||||
try { ws.send(JSON.stringify({ action: 'previousLogins', userid: command.userid, events: e })); } catch (ex) { }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -913,6 +913,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
||||
var jsondata = null, jsondatastr = data.substring(5, 5 + jsondatalen);
|
||||
try { jsondata = JSON.parse(jsondatastr); } catch (ex) { }
|
||||
if ((jsondata == null) || (typeof jsondata.action != 'string')) return;
|
||||
parent.debug('mpscmd', '--> JSON_CONTROL', jsondata.action);
|
||||
switch (jsondata.action) {
|
||||
case 'connType':
|
||||
if ((socket.tag.connType != 0) || (socket.tag.SystemId != null)) return; // Once set, the connection type can't be changed.
|
||||
@ -927,6 +928,8 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
||||
if (obj.parent.amtManager != null) { obj.parent.amtManager.mpsControlMessage(socket.tag.nodeid, socket, socket.tag.connType, jsondata); }
|
||||
break;
|
||||
case 'deactivate':
|
||||
case 'startTlsHostConfig':
|
||||
case 'stopConfiguration':
|
||||
if (socket.tag.connType != 2) break; // Only accept MEI state on CIRA-LMS connection
|
||||
if (obj.parent.amtManager != null) { obj.parent.amtManager.mpsControlMessage(socket.tag.nodeid, socket, socket.tag.connType, jsondata); }
|
||||
break;
|
||||
@ -956,8 +959,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
||||
|
||||
obj.SendJsonControl = function(socket, data) {
|
||||
if (socket.tag.connType == 0) return; // This command is valid only for connections that are not really CIRA.
|
||||
parent.debug('mpscmd', '<-- JSON_CONTROL');
|
||||
if (typeof data == 'object') { data = JSON.stringify(data); }
|
||||
if (typeof data == 'object') { parent.debug('mpscmd', '<-- JSON_CONTROL', data.action); data = JSON.stringify(data); } else { parent.debug('mpscmd', '<-- JSON_CONTROL'); }
|
||||
Write(socket, String.fromCharCode(APFProtocol.JSON_CONTROL) + common.IntToStr(data.length) + data);
|
||||
}
|
||||
|
||||
|
1
node11.bat
Normal file
1
node11.bat
Normal file
@ -0,0 +1 @@
|
||||
@%LOCALAPPDATA%\..\Roaming\nvm\v11.10.1\node meshcentral %1 %2 %3 %4 %5 %6 %7 %8 %9
|
1
node15.bat
Normal file
1
node15.bat
Normal file
@ -0,0 +1 @@
|
||||
@%LOCALAPPDATA%\..\Roaming\nvm\v15.0.1\node meshcentral %1 %2 %3 %4 %5 %6 %7 %8 %9
|
1
node8.bat
Normal file
1
node8.bat
Normal file
@ -0,0 +1 @@
|
||||
@%LOCALAPPDATA%\..\Roaming\nvm\v8.0.0\node meshcentral %1 %2 %3 %4 %5 %6 %7 %8 %9
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.7.78",
|
||||
"version": "0.7.81",
|
||||
"keywords": [
|
||||
"Remote Device Management",
|
||||
"Remote Device Monitoring",
|
||||
|
2
public/scripts/amt-0.2.0-min.js
vendored
2
public/scripts/amt-0.2.0-min.js
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user