Merge branch 'master' of https://github.com/Ylianst/MeshCentral into master

This commit is contained in:
Bryan Roe 2021-03-05 17:31:00 -08:00
commit dea0d96add
113 changed files with 96567 additions and 2432 deletions

View File

@ -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.

View File

@ -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.",

View File

@ -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; } });

View File

@ -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

View File

@ -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)

View File

@ -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,
*/

View File

@ -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)

View File

@ -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;

View File

@ -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,
*/

View File

@ -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

View File

@ -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);

View File

@ -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...");

View File

@ -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); }

View File

@ -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' };

View File

@ -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
View File

@ -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;

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>&nbsp;&nbsp;&nbsp;Uživatelské jméno: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;Heslo: <b notrans=1>[[[PASSWORD]]]</b></p>S pozdravem,<br>[[[USERNAME]]]<br></div>

View 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>&nbsp;&nbsp;&nbsp;Benutzername: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;Passwort: <b notrans=1>[[[PASSWORD]]]</b></p>Freundliche Grüße,<br>[[[USERNAME]]]<br></div>

View 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>&nbsp;&nbsp;&nbsp;Nombre de usuario: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;Contraseña: <b notrans=1>[[[PASSWORD]]]</b></p>Atentamente,<br>[[[USERNAME]]]<br></div>

View 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>&nbsp;&nbsp;&nbsp;Käyttäjätunnus: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;Salasana: <b notrans=1>[[[PASSWORD]]]</b></p>Ystävällisin terveisin,<br>[[[USERNAME]]]<br></div>

View 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>&nbsp;&nbsp;&nbsp;Nom d'utilisateur: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;Mot de passe: <b notrans=1>[[[PASSWORD]]]</b></p>Meilleures salutations,<br>[[[USERNAME]]]<br></div>

View 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>&nbsp;&nbsp;&nbsp;उपयोगकर्ता नाम: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;कुंजिका: <b notrans=1>[[[PASSWORD]]]</b></p>सादर,<br>[[[USERNAME]]]<br></div>

View 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>&nbsp;&nbsp;&nbsp;ユーザー名: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;パスワード: <b notrans=1>[[[PASSWORD]]]</b></p>宜しくお願いします、<br>[[[USERNAME]]]<br></div>

View 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>&nbsp;&nbsp;&nbsp;사용자 이름: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;암호: <b notrans=1>[[[PASSWORD]]]</b></p>최고의 안부를 전합니다,<br>[[[USERNAME]]]<br></div>

View 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>&nbsp;&nbsp;&nbsp;Gebruikersnaam: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;Wachtwoord: <b notrans=1>[[[PASSWORD]]]</b></p>Vriendelijke groeten,<br>[[[USERNAME]]]<br></div>

View 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>&nbsp;&nbsp;&nbsp;Nome de usuário: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;Senha: <b notrans=1>[[[PASSWORD]]]</b></p>Cumprimentos,<br>[[[NOME DO USUÁRIO]]]<br></div>

View 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>&nbsp;&nbsp;&nbsp;Имя пользователя: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;Пароль: <b notrans=1>[[[PASSWORD]]]</b></p>С уважением,<br>[[[USERNAME]]]<br></div>

View 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>&nbsp;&nbsp;&nbsp;Kullanıcı adı: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;Parola: <b notrans=1>[[[PASSWORD]]]</b></p>Saygılarımla,<br>[[[USERNAME]]]<br></div>

View 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>&nbsp;&nbsp;&nbsp;用户名: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;密码: <b notrans=1>[[[PASSWORD]]]</b></p>最好的祝福,<br>[[[USERNAME]]]<br></div>

View 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>&nbsp;&nbsp;&nbsp;用戶名: <b notrans=1>[[[ACCOUNTNAME]]]</b><br>&nbsp;&nbsp;&nbsp;密碼: <b notrans=1>[[[PASSWORD]]]</b></p>最好的祝福,<br>[[[USERNAME]]]<br></div>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;installflags=[[[INSTALLFLAGS]]]">विंडोज के लिए मेशजेंट को डाउनलोड करने के लिए यहां क्लिक करें।</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;installflags=[[[INSTALLFLAGS]]]">Windows用のMeshAgentをダウンロードするには、ここをクリックしてください。</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;installflags=[[[INSTALLFLAGS]]]">Windows를 위한 MeshAgent를 다운로드 하려면 여기를 클릭하십시오.</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;installflags=[[[INSTALLFLAGS]]]">Нажмите здесь, чтобы загрузить MeshAgent для Windows.</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;installflags=[[[INSTALLFLAGS]]]">单击此处下载适用于Windows的MeshAgent。</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View 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&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;installflags=[[[INSTALLFLAGS]]]">單擊此處下載適用於Windows的MeshAgent。</a></p></area-windows><area-osx><p style=margin-left:30px><a href="[[[SERVERURL]]]/meshosxagent?id=16&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;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 &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; 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>

View File

@ -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'); }
});

View File

@ -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; }

View File

@ -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",

View File

@ -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);
}
}

View File

@ -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) { }
});
}
}
}
}

View File

@ -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
View 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
View 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
View File

@ -0,0 +1 @@
@%LOCALAPPDATA%\..\Roaming\nvm\v8.0.0\node meshcentral %1 %2 %3 %4 %5 %6 %7 %8 %9

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.7.78",
"version": "0.7.81",
"keywords": [
"Remote Device Management",
"Remote Device Monitoring",

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