Merge remote-tracking branch 'upstream/master'

This commit is contained in:
jsastriawan 2020-04-09 08:47:27 -07:00
commit 78ebbc0188
202 changed files with 16420 additions and 3425 deletions

View File

@ -104,6 +104,7 @@
<Compile Include="apfserver.js" />
<Compile Include="exeHandler.js" />
<Compile Include="letsencrypt.js" />
<Compile Include="mcrec.js" />
<Compile Include="meshaccelerator.js" />
<Compile Include="meshctrl.js" />
<Compile Include="meshmail.js" />
@ -181,6 +182,181 @@
<Content Include="public\clickonce\minirouter\publish.htm" />
<Content Include="public\commander.htm" />
<Content Include="public\compress.wcc" />
<Content Include="public\email\account-check.html" />
<Content Include="public\email\account-check.txt" />
<Content Include="public\email\account-invite.html" />
<Content Include="public\email\account-invite.txt" />
<Content Include="public\email\account-login.html" />
<Content Include="public\email\account-login.txt" />
<Content Include="public\email\account-reset.html" />
<Content Include="public\email\account-reset.txt" />
<Content Include="public\email\mesh-invite.html" />
<Content Include="public\email\mesh-invite.txt" />
<Content Include="public\email\translations\account-check-min_cs.html" />
<Content Include="public\email\translations\account-check-min_de.html" />
<Content Include="public\email\translations\account-check-min_es.html" />
<Content Include="public\email\translations\account-check-min_fr.html" />
<Content Include="public\email\translations\account-check-min_hi.html" />
<Content Include="public\email\translations\account-check-min_ja.html" />
<Content Include="public\email\translations\account-check-min_ko.html" />
<Content Include="public\email\translations\account-check-min_nl.html" />
<Content Include="public\email\translations\account-check-min_pt.html" />
<Content Include="public\email\translations\account-check-min_ru.html" />
<Content Include="public\email\translations\account-check-min_zh-chs.html" />
<Content Include="public\email\translations\account-check_cs.html" />
<Content Include="public\email\translations\account-check_cs.txt" />
<Content Include="public\email\translations\account-check_de.html" />
<Content Include="public\email\translations\account-check_de.txt" />
<Content Include="public\email\translations\account-check_es.html" />
<Content Include="public\email\translations\account-check_es.txt" />
<Content Include="public\email\translations\account-check_fr.html" />
<Content Include="public\email\translations\account-check_fr.txt" />
<Content Include="public\email\translations\account-check_hi.html" />
<Content Include="public\email\translations\account-check_hi.txt" />
<Content Include="public\email\translations\account-check_ja.html" />
<Content Include="public\email\translations\account-check_ja.txt" />
<Content Include="public\email\translations\account-check_ko.html" />
<Content Include="public\email\translations\account-check_ko.txt" />
<Content Include="public\email\translations\account-check_nl.html" />
<Content Include="public\email\translations\account-check_nl.txt" />
<Content Include="public\email\translations\account-check_pt.html" />
<Content Include="public\email\translations\account-check_pt.txt" />
<Content Include="public\email\translations\account-check_ru.html" />
<Content Include="public\email\translations\account-check_ru.txt" />
<Content Include="public\email\translations\account-check_zh-chs.html" />
<Content Include="public\email\translations\account-check_zh-chs.txt" />
<Content Include="public\email\translations\account-invite-min_cs.html" />
<Content Include="public\email\translations\account-invite-min_de.html" />
<Content Include="public\email\translations\account-invite-min_es.html" />
<Content Include="public\email\translations\account-invite-min_fr.html" />
<Content Include="public\email\translations\account-invite-min_hi.html" />
<Content Include="public\email\translations\account-invite-min_ja.html" />
<Content Include="public\email\translations\account-invite-min_ko.html" />
<Content Include="public\email\translations\account-invite-min_nl.html" />
<Content Include="public\email\translations\account-invite-min_pt.html" />
<Content Include="public\email\translations\account-invite-min_ru.html" />
<Content Include="public\email\translations\account-invite-min_zh-chs.html" />
<Content Include="public\email\translations\account-invite_cs.html" />
<Content Include="public\email\translations\account-invite_cs.txt" />
<Content Include="public\email\translations\account-invite_de.html" />
<Content Include="public\email\translations\account-invite_de.txt" />
<Content Include="public\email\translations\account-invite_es.html" />
<Content Include="public\email\translations\account-invite_es.txt" />
<Content Include="public\email\translations\account-invite_fr.html" />
<Content Include="public\email\translations\account-invite_fr.txt" />
<Content Include="public\email\translations\account-invite_hi.html" />
<Content Include="public\email\translations\account-invite_hi.txt" />
<Content Include="public\email\translations\account-invite_ja.html" />
<Content Include="public\email\translations\account-invite_ja.txt" />
<Content Include="public\email\translations\account-invite_ko.html" />
<Content Include="public\email\translations\account-invite_ko.txt" />
<Content Include="public\email\translations\account-invite_nl.html" />
<Content Include="public\email\translations\account-invite_nl.txt" />
<Content Include="public\email\translations\account-invite_pt.html" />
<Content Include="public\email\translations\account-invite_pt.txt" />
<Content Include="public\email\translations\account-invite_ru.html" />
<Content Include="public\email\translations\account-invite_ru.txt" />
<Content Include="public\email\translations\account-invite_zh-chs.html" />
<Content Include="public\email\translations\account-invite_zh-chs.txt" />
<Content Include="public\email\translations\account-login-min_cs.html" />
<Content Include="public\email\translations\account-login-min_de.html" />
<Content Include="public\email\translations\account-login-min_es.html" />
<Content Include="public\email\translations\account-login-min_fr.html" />
<Content Include="public\email\translations\account-login-min_hi.html" />
<Content Include="public\email\translations\account-login-min_ja.html" />
<Content Include="public\email\translations\account-login-min_ko.html" />
<Content Include="public\email\translations\account-login-min_nl.html" />
<Content Include="public\email\translations\account-login-min_pt.html" />
<Content Include="public\email\translations\account-login-min_ru.html" />
<Content Include="public\email\translations\account-login-min_zh-chs.html" />
<Content Include="public\email\translations\account-login_cs.html" />
<Content Include="public\email\translations\account-login_cs.txt" />
<Content Include="public\email\translations\account-login_de.html" />
<Content Include="public\email\translations\account-login_de.txt" />
<Content Include="public\email\translations\account-login_es.html" />
<Content Include="public\email\translations\account-login_es.txt" />
<Content Include="public\email\translations\account-login_fr.html" />
<Content Include="public\email\translations\account-login_fr.txt" />
<Content Include="public\email\translations\account-login_hi.html" />
<Content Include="public\email\translations\account-login_hi.txt" />
<Content Include="public\email\translations\account-login_ja.html" />
<Content Include="public\email\translations\account-login_ja.txt" />
<Content Include="public\email\translations\account-login_ko.html" />
<Content Include="public\email\translations\account-login_ko.txt" />
<Content Include="public\email\translations\account-login_nl.html" />
<Content Include="public\email\translations\account-login_nl.txt" />
<Content Include="public\email\translations\account-login_pt.html" />
<Content Include="public\email\translations\account-login_pt.txt" />
<Content Include="public\email\translations\account-login_ru.html" />
<Content Include="public\email\translations\account-login_ru.txt" />
<Content Include="public\email\translations\account-login_zh-chs.html" />
<Content Include="public\email\translations\account-login_zh-chs.txt" />
<Content Include="public\email\translations\account-reset-min_cs.html" />
<Content Include="public\email\translations\account-reset-min_de.html" />
<Content Include="public\email\translations\account-reset-min_es.html" />
<Content Include="public\email\translations\account-reset-min_fr.html" />
<Content Include="public\email\translations\account-reset-min_hi.html" />
<Content Include="public\email\translations\account-reset-min_ja.html" />
<Content Include="public\email\translations\account-reset-min_ko.html" />
<Content Include="public\email\translations\account-reset-min_nl.html" />
<Content Include="public\email\translations\account-reset-min_pt.html" />
<Content Include="public\email\translations\account-reset-min_ru.html" />
<Content Include="public\email\translations\account-reset-min_zh-chs.html" />
<Content Include="public\email\translations\account-reset_cs.html" />
<Content Include="public\email\translations\account-reset_cs.txt" />
<Content Include="public\email\translations\account-reset_de.html" />
<Content Include="public\email\translations\account-reset_de.txt" />
<Content Include="public\email\translations\account-reset_es.html" />
<Content Include="public\email\translations\account-reset_es.txt" />
<Content Include="public\email\translations\account-reset_fr.html" />
<Content Include="public\email\translations\account-reset_fr.txt" />
<Content Include="public\email\translations\account-reset_hi.html" />
<Content Include="public\email\translations\account-reset_hi.txt" />
<Content Include="public\email\translations\account-reset_ja.html" />
<Content Include="public\email\translations\account-reset_ja.txt" />
<Content Include="public\email\translations\account-reset_ko.html" />
<Content Include="public\email\translations\account-reset_ko.txt" />
<Content Include="public\email\translations\account-reset_nl.html" />
<Content Include="public\email\translations\account-reset_nl.txt" />
<Content Include="public\email\translations\account-reset_pt.html" />
<Content Include="public\email\translations\account-reset_pt.txt" />
<Content Include="public\email\translations\account-reset_ru.html" />
<Content Include="public\email\translations\account-reset_ru.txt" />
<Content Include="public\email\translations\account-reset_zh-chs.html" />
<Content Include="public\email\translations\account-reset_zh-chs.txt" />
<Content Include="public\email\translations\mesh-invite-min_cs.html" />
<Content Include="public\email\translations\mesh-invite-min_de.html" />
<Content Include="public\email\translations\mesh-invite-min_es.html" />
<Content Include="public\email\translations\mesh-invite-min_fr.html" />
<Content Include="public\email\translations\mesh-invite-min_hi.html" />
<Content Include="public\email\translations\mesh-invite-min_ja.html" />
<Content Include="public\email\translations\mesh-invite-min_ko.html" />
<Content Include="public\email\translations\mesh-invite-min_nl.html" />
<Content Include="public\email\translations\mesh-invite-min_pt.html" />
<Content Include="public\email\translations\mesh-invite-min_ru.html" />
<Content Include="public\email\translations\mesh-invite-min_zh-chs.html" />
<Content Include="public\email\translations\mesh-invite_cs.html" />
<Content Include="public\email\translations\mesh-invite_cs.txt" />
<Content Include="public\email\translations\mesh-invite_de.html" />
<Content Include="public\email\translations\mesh-invite_de.txt" />
<Content Include="public\email\translations\mesh-invite_es.html" />
<Content Include="public\email\translations\mesh-invite_es.txt" />
<Content Include="public\email\translations\mesh-invite_fr.html" />
<Content Include="public\email\translations\mesh-invite_fr.txt" />
<Content Include="public\email\translations\mesh-invite_hi.html" />
<Content Include="public\email\translations\mesh-invite_hi.txt" />
<Content Include="public\email\translations\mesh-invite_ja.html" />
<Content Include="public\email\translations\mesh-invite_ja.txt" />
<Content Include="public\email\translations\mesh-invite_ko.html" />
<Content Include="public\email\translations\mesh-invite_ko.txt" />
<Content Include="public\email\translations\mesh-invite_nl.html" />
<Content Include="public\email\translations\mesh-invite_nl.txt" />
<Content Include="public\email\translations\mesh-invite_pt.html" />
<Content Include="public\email\translations\mesh-invite_pt.txt" />
<Content Include="public\email\translations\mesh-invite_ru.html" />
<Content Include="public\email\translations\mesh-invite_ru.txt" />
<Content Include="public\email\translations\mesh-invite_zh-chs.html" />
<Content Include="public\email\translations\mesh-invite_zh-chs.txt" />
<Content Include="public\favicon.ico" />
<Content Include="public\images-isdu\ComputerIcon.png" />
<Content Include="public\images-isdu\ComputerIcon2.png" />
@ -281,6 +457,7 @@
<Content Include="views\download.handlebars" />
<Content Include="views\error404-mobile.handlebars" />
<Content Include="views\error404.handlebars" />
<Content Include="views\invite.handlebars" />
<Content Include="views\login-mobile.handlebars" />
<Content Include="views\login.handlebars" />
<Content Include="views\message.handlebars" />
@ -301,6 +478,8 @@
<Folder Include="public\clickonce\minirouter\" />
<Folder Include="public\clickonce\minirouter\Application Files\" />
<Folder Include="public\clickonce\minirouter\Application Files\MeshMiniRouter_1_0_0_70\" />
<Folder Include="public\email\" />
<Folder Include="public\email\translations\" />
<Folder Include="public\images-isdu" />
<Folder Include="public\images\" />
<Folder Include="public\scripts\" />
@ -315,6 +494,7 @@
<Folder Include="typings\" />
<Folder Include="typings\globals\" />
<Folder Include="typings\globals\connect-redis\" />
<Folder Include="typings\globals\cookie-session\" />
<Folder Include="typings\globals\express-handlebars\" />
<Folder Include="typings\globals\express-session\" />
<Folder Include="typings\globals\node-forge\" />
@ -324,6 +504,7 @@
</ItemGroup>
<ItemGroup>
<TypeScriptCompile Include="typings\globals\connect-redis\index.d.ts" />
<TypeScriptCompile Include="typings\globals\cookie-session\index.d.ts" />
<TypeScriptCompile Include="typings\globals\express-handlebars\index.d.ts" />
<TypeScriptCompile Include="typings\globals\express-session\index.d.ts" />
<TypeScriptCompile Include="typings\globals\node-forge\index.d.ts" />

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
agents/meshagent_arm-linaro Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -153,8 +153,10 @@ function run(argv) {
if ((typeof args.cdrom) == 'string') { settings.cdrom = args.cdrom; }
if ((typeof args.tag) == 'string') { settings.tag = args.tag; }
if ((typeof args.scan) == 'string') { settings.scan = args.scan; }
if ((typeof args.token) == 'string') { settings.token = args.token; }
if ((typeof args.timeout) == 'string') { settings.timeout = parseInt(args.timeout); }
if ((typeof args.uuidoutput) == 'string' || args.uuidoutput) { settings.uuidoutput = args.uuidoutput; }
if (args.emailtoken) { settings.emailtoken = true; }
if (args.debug === true) { settings.debuglevel = 1; }
if (args.debug) { try { waitForDebugger(); } catch (e) { } }
if (args.noconsole) { settings.noconsole = true; }
@ -591,10 +593,11 @@ function run(argv) {
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
if ((settings.hostname == null) || (typeof settings.hostname != 'string') || (settings.hostname == '')) { settings.hostname = '127.0.0.1'; }
if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { settings.username = 'admin'; }
if ((settings.script == null) || (typeof settings.script != 'string') || (settings.script == '')) { if (mescriptJSON != '') { settings.scriptjson = mescriptJSON; } else { console.log('No or invalid \"script\" file specified, use --script [filename].'); exit(1); return; } }
//if ((settings.script == null) || (typeof settings.script != 'string') || (settings.script == '')) { if (mescriptJSON != '') { settings.scriptjson = mescriptJSON; } else { console.log('No or invalid \"script\" file specified, use --script [filename].'); exit(1); return; } }
if ((settings.script == null) || (typeof settings.script != 'string') || (settings.script == '')) { console.log('No or invalid \"script\" file specified, use --script [filename].'); exit(1); return; }
startMeScript();
} else if (settings.action == 'amtuuid') {
// Start running
// Start running
if (settings.hostname != null) {
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { settings.username = 'admin'; }
@ -1021,7 +1024,8 @@ function startMeshCommander() {
} else {
// If TLS is going to be used, setup a TLS socket
var tls = require('tls');
var tlsoptions = { host: webargs.host, port: webargs.port, secureProtocol: ((webargs.tls1only == 1) ? 'TLSv1_method' : 'SSLv23_method'), rejectUnauthorized: false };
var tlsoptions = { host: webargs.host, port: webargs.port, rejectUnauthorized: false };
if (webargs.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
ws.forwardclient = tls.connect(tlsoptions, function () { debug(1, 'Connected TLS to ' + webargs.host + ':' + webargs.port + '.'); this.pipe(this.ws, { end: false }); this.ws.pipe(this, { end: false }); });
ws.forwardclient.on('error', function () { debug(1, 'TLS connection error to ' + webargs.host + ':' + webargs.port + '.'); try { this.ws.end(); } catch (e) { } });
ws.forwardclient.ws = ws;
@ -2046,20 +2050,80 @@ function processLmsControlData(data) {
//
function startRouter() {
// Start by requesting a login token, this is needed because of 2FA and check that we have correct credentials from the start
var options;
try {
var url = settings.serverurl.split('meshrelay.ashx').join('control.ashx') + '?user=' + settings.username + '&pass=' + settings.password;
if (settings.emailtoken) { url += '&token=**email**'; } else if (settings.token != null) { url += '&token=' + settings.token; }
options = http.parseUri(url);
} catch (e) { console.log("Unable to parse \"serverUrl\"."); process.exit(1); return; }
options.checkServerIdentity = onVerifyServer;
options.rejectUnauthorized = false;
settings.websocket = http.request(options);
settings.websocket.upgrade = OnServerWebSocket;
settings.websocket.on('error', function (e) { console.log("ERROR: " + JSON.stringify(e)); });
settings.websocket.end();
}
function OnServerWebSocket(msg, s, head) {
settings.webchannel = s;
s.on('data', function (msg) {
var command = JSON.parse(msg);
switch (command.action) {
case 'close': {
if (command.cause == 'noauth') {
if (command.msg == 'tokenrequired') {
if (command.email2fasent === true) {
console.log("Login token email sent.");
} else if (command.email2fa === true) {
console.log("Login token required, use --token [token], or --emailtoken get a token.");
} else {
console.log("Login token required, use --token [token].");
}
} else { console.log("Invalid username or password."); }
} else { console.log("Server disconnected: " + command.msg); }
process.exit(1);
return;
}
case 'serverinfo': {
s.write("{\"action\":\"authcookie\"}"); // Ask for our first authentication cookie
break;
}
case 'authcookie': {
if (settings.acookie == null) {
settings.acookie = command.cookie;
settings.rcookie = command.rcookie;
settings.renewCookieTimer = setInterval(function () { settings.webchannel.write("{\"action\":\"authcookie\"}"); }, 600000); // Ask for new cookie every 10 minutes
startRouterEx();
} else {
settings.acookie = command.cookie;
settings.rcookie = command.rcookie;
}
break;
}
}
});
s.on('error', function () { console.log("Server connection error."); process.exit(1); return; });
s.on('close', function () { console.log("Server closed the connection."); process.exit(1); return; });
}
function startRouterEx() {
tcpserver = net.createServer(OnTcpClientConnected);
tcpserver.on('error', function (e) { console.log("ERROR: " + JSON.stringify(e)); exit(0); return; });
tcpserver.listen(settings.localport, function () {
// We started listening.
if (settings.remotetarget == null) {
console.log('Redirecting local port ' + settings.localport + ' to remote port ' + settings.remoteport + '.');
} else {
console.log('Redirecting local port ' + settings.localport + ' to ' + settings.remotetarget + ':' + settings.remoteport + '.');
}
console.log("Press ctrl-c to exit.");
try {
tcpserver.listen(settings.localport, function () {
// We started listening.
if (settings.remotetarget == null) {
console.log('Redirecting local port ' + settings.localport + ' to remote port ' + settings.remoteport + '.');
} else {
console.log('Redirecting local port ' + settings.localport + ' to ' + settings.remotetarget + ':' + settings.remoteport + '.');
}
console.log("Press ctrl-c to exit.");
// If settings has a "cmd", run it now.
//process.exec("notepad.exe");
});
// If settings has a "cmd", run it now.
//process.exec("notepad.exe");
});
} catch (ex) { console.log("Unable to bind to local TCP port " + settings.localport + "."); exit(1); return; }
}
// Called when a TCP connect is received on the local port. Launch a tunnel.
@ -2069,8 +2133,9 @@ function OnTcpClientConnected(c) {
debug(1, "Client connected");
c.on('end', function () { disconnectTunnel(this, this.websocket, "Client closed"); });
c.pause();
var options;
try {
options = http.parseUri(settings.serverurl + '?user=' + settings.username + '&pass=' + settings.password + '&nodeid=' + settings.remotenodeid + '&tcpport=' + settings.remoteport + (settings.remotetarget == null ? '' : '&tcpaddr=' + settings.remotetarget));
options = http.parseUri(settings.serverurl + '?auth=' + settings.acookie + '&nodeid=' + settings.remotenodeid + '&tcpport=' + settings.remoteport + (settings.remotetarget == null ? '' : '&tcpaddr=' + settings.remotetarget));
} catch (e) { console.log("Unable to parse \"serverUrl\"."); process.exit(1); return; }
options.checkServerIdentity = onVerifyServer;
options.rejectUnauthorized = false;
@ -2104,8 +2169,8 @@ function OnWebSocket(msg, s, head) {
}
}
});
s.on('error', function (msg) { disconnectTunnel(this.tcp, this, 'Websocket error'); });
s.on('close', function (msg) { disconnectTunnel(this.tcp, this, 'Websocket closed'); });
s.on('error', function () { disconnectTunnel(this.tcp, this, 'Websocket error'); });
s.on('close', function () { disconnectTunnel(this.tcp, this, 'Websocket closed'); });
s.parent = this;
}

View File

@ -36,6 +36,10 @@ var MESHRIGHT_NOTERMINAL = 512;
var MESHRIGHT_NOFILES = 1024;
var MESHRIGHT_NOAMT = 2048;
var MESHRIGHT_LIMITEDINPUT = 4096;
var MESHRIGHT_LIMITEVENTS = 8192;
var MESHRIGHT_CHATNOTIFY = 16384;
var MESHRIGHT_UNINSTALL = 32768;
var MESHRIGHT_NODESKTOP = 65536;
function createMeshCore(agent) {
var obj = {};
@ -276,7 +280,8 @@ function createMeshCore(agent) {
*/
// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
var meshCoreObj = { action: 'coreinfo', value: 'MeshCore v6', caps: 14 }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ('MeshCore CRC[' + crc32c(require('MeshAgent').coreHash) + ']') : ('MeshCore v6')), caps: 14 }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
// Get the operating system description string
try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; }); } catch (ex) { }
@ -790,7 +795,7 @@ function createMeshCore(agent) {
if (process.platform != 'win32') break;
var p = require('user-sessions').enumerateUsers();
p.sessionid = data.sessionid;
p.then(function (u) { mesh.SendCommand({ action: 'msg', type: 'userSessions', sessionid: u.sessionid, data: u }); });
p.then(function (u) { mesh.SendCommand({ action: 'msg', type: 'userSessions', sessionid: data.sessionid, data: u }); });
break;
}
default:
@ -1328,7 +1333,7 @@ function createMeshCore(agent) {
//this.write('MeshCore Terminal Hello');
} else if (this.httprequest.protocol == 2) {
// Check user access rights for desktop
if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) {
if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0))) {
// Disengage this tunnel, user does not have the rights to do this!!
this.httprequest.protocol = 999999;
this.httprequest.s.end();
@ -1396,8 +1401,7 @@ function createMeshCore(agent) {
if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount')) {
this.httprequest.desktop.kvm.connectionCount++;
this.httprequest.desktop.kvm.users.push(this.httprequest.username);
}
else {
} else {
this.httprequest.desktop.kvm.connectionCount = 1;
this.httprequest.desktop.kvm.users = [this.httprequest.username];
}
@ -1505,7 +1509,6 @@ function createMeshCore(agent) {
//this.write('MeshCore KVM Hello!1');
} else if (this.httprequest.protocol == 5) {
// Check user access rights for files
if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0))) {
// Disengage this tunnel, user does not have the rights to do this!!
@ -1958,7 +1961,7 @@ function createMeshCore(agent) {
var response = null;
switch (cmd) {
case 'help': { // Displays available commands
var fin = '', f = '', availcommands = 'agentsize,version,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper';
var fin = '', f = '', availcommands = 'startupoptions,alert,agentsize,version,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper';
if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration'; }
availcommands = availcommands.split(',').sort();
while (availcommands.length > 0) {
@ -1969,6 +1972,27 @@ function createMeshCore(agent) {
response = "Available commands: \r\n" + fin + ".";
break;
}
case 'startupoptions':
response = JSON.stringify(require('MeshAgent').getStartupOptions());
break;
case 'alert':
if (args['_'].length == 0)
{
response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
}
else
{
var p = args['_'].join(' ').split(',');
if(p.length<2)
{
response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
}
else
{
this._alert = require('message-box').create(p[0], p[1], p.length==3?parseInt(p[2]):9999,1);
}
}
break;
case 'agentsize':
var actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
if (process.platform == 'win32') {
@ -2366,7 +2390,7 @@ function createMeshCore(agent) {
break;
}
case 'info': { // Return information about the agent and agent core module
response = 'Current Core: ' + meshCoreObj.value + '.\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform: ' + process.platform + '.\r\nCapabilities: ' + meshCoreObj.caps + '.\r\nServer URL: ' + mesh.ServerUrl + '.';
response = 'Current Core: ' + meshCoreObj.value + '\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform: ' + process.platform + '.\r\nCapabilities: ' + meshCoreObj.caps + '.\r\nServer URL: ' + mesh.ServerUrl + '.';
if (amt != null) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amt.lmsstate] + '.'; }
if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; }
response += '\r\nModules: ' + addedModules.join(', ') + '.';

View File

@ -75,7 +75,15 @@ CheckInstallAgent() {
machineid=30
else
# Linux x86, 64 bit
machineid=6
bitlen=$( getconf LONG_BIT )
if [ $bitlen == '32' ]
then
# 32 bit OS
machineid=5
else
# 64 bit OS
machineid=6
fi
fi
fi
if [ $machinetype == 'x86' ] || [ $machinetype == 'i686' ] || [ $machinetype == 'i586' ]

View File

@ -21,8 +21,7 @@ limitations under the License.
*/
// Construct a WSMAN communication object
function CreateWsmanComm(/*host, port, user, pass, tls, extra*/)
{
function CreateWsmanComm(/*host, port, user, pass, tls, extra*/) {
var obj = {};
obj.PendingAjax = []; // List of pending AJAX calls. When one frees up, another will start.
obj.ActiveAjaxCount = 0; // Number of currently active AJAX calls
@ -31,15 +30,13 @@ function CreateWsmanComm(/*host, port, user, pass, tls, extra*/)
obj.digest = null;
obj.RequestCount = 0;
if (arguments.length == 1 && typeof(arguments[0] == 'object'))
{
if (arguments.length == 1 && typeof (arguments[0] == 'object')) {
obj.host = arguments[0].host;
obj.port = arguments[0].port;
obj.authToken = arguments[0].authToken;
obj.tls = arguments[0].tls;
}
else
{
else {
obj.host = arguments[0];
obj.port = arguments[1];
obj.user = arguments[2];
@ -75,14 +72,10 @@ function CreateWsmanComm(/*host, port, user, pass, tls, extra*/)
if (globalDebugFlags & 1) { console.log("SEND: " + postdata + "\r\n\r\n"); } // DEBUG
// We are in a DukTape environement
if (obj.digest == null)
{
if (obj.authToken)
{
if (obj.digest == null) {
if (obj.authToken) {
obj.digest = require('http-digest').create({ authToken: obj.authToken });
}
else
{
} else {
obj.digest = require('http-digest').create(obj.user, obj.pass);
}
obj.digest.http = require('http');

View File

@ -191,6 +191,12 @@ function SMBiosTables()
catch(e)
{
}
try
{
if (JSON.stringify(r).length > 65535) { r = {}; }
}
catch(ee)
{}
return r;
}
this.processorInfo = function processorInfo(data) {

View File

@ -145,7 +145,6 @@ function WindowsConsole()
this.TrayIcon.remove();
handled = true;
}
//if (!handled) { console.log(msg); }
}
});
retVal.remove = function remove()

View File

@ -401,7 +401,8 @@ function windows_terminal() {
}
}
}
this._WriteCharacter = function (key, bControlKey) {
this._WriteCharacter = function (key, bControlKey)
{
var rec = GM.CreateVariable(20);
rec.Deref(0, 2).toBuffer().writeUInt16LE(KEY_EVENT); // rec.EventType
rec.Deref(4, 4).toBuffer().writeUInt16LE(1); // rec.Event.KeyEvent.bKeyDown
@ -410,10 +411,10 @@ function windows_terminal() {
rec.Deref(8, 2).toBuffer().writeUInt16LE(1); // rec.Event.KeyEvent.wRepeatCount
rec.Deref(10, 2).toBuffer().writeUInt16LE(this._user32.VkKeyScanA(key).Val); // rec.Event.KeyEvent.wVirtualKeyCode
rec.Deref(12, 2).toBuffer().writeUInt16LE(this._user32.MapVirtualKeyA(this._user32.VkKeyScanA(key).Val, MAPVK_VK_TO_VSC).Val);
var dwWritten = GM.CreateVariable(4);
if (this._kernel32.WriteConsoleInputA(this._stdinput, rec, 1, dwWritten).Val == 0) { return (false); }
rec.Deref(4, 4).toBuffer().writeUInt16LE(0); // rec.Event.KeyEvent.bKeyDown
return (this._kernel32.WriteConsoleInputA(this._stdinput, rec, 1, dwWritten).Val != 0);
}
@ -478,23 +479,28 @@ function windows_terminal() {
return (retVal);
}
this._SendDataBuffer = function (data) {
this._SendDataBuffer = function (data)
{
// { data, attributes, width, height, x, y }
var dy, line, attr;
for (dy = 0; dy < data.height; ++dy) {
line = data.data[dy];
attr = data.attributes[dy];
line.s = line.toString();
//line = data.data.slice(data.width * dy, (data.width * dy) + data.width);
//attr = data.attributes.slice(data.width * dy, (data.width * dy) + data.width);
this._stream.push(TranslateLine(data.x + 1, data.y + dy + 1, line, attr));
if (this._stream != null)
{
var dy, line, attr;
for (dy = 0; dy < data.height; ++dy)
{
line = data.data[dy];
attr = data.attributes[dy];
line.s = line.toString();
//line = data.data.slice(data.width * dy, (data.width * dy) + data.width);
//attr = data.attributes.slice(data.width * dy, (data.width * dy) + data.width);
this._stream.push(TranslateLine(data.x + 1, data.y + dy + 1, line, attr));
}
}
}
this._SendScroll = function _SendScroll(dx, dy) {
if (this._scrollTimer) { return; }
this._SendScroll = function _SendScroll(dx, dy)
{
if (this._scrollTimer || this._stream == null) { return; }
var info = GM.CreateVariable(22);
if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, info).Val == 0) { throw ('Error getting screen buffer info'); }

View File

@ -607,7 +607,7 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
if (g_len > obj.iderinfo.readbfr) { len = obj.iderinfo.readbfr; }
g_len -= len;
g_lba += len;
var buffer = new Buffer(len);
var buffer = Buffer.alloc(len);
fs.read(g_media, buffer, 0, len, lba, function (error, bytesRead, buffer) {
obj.SendDataToHost(g_dev, (g_len == 0), buffer.toString('binary'), featureRegister & 1);
if ((g_len > 0) && (g_reset == false)) {

View File

@ -150,7 +150,8 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
// TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
const TLSSocket = require('tls').TLSSocket;
const tlsoptions = { secureProtocol: ((obj.tls1only == 1) ? 'TLSv1_method' : 'SSLv23_method'), ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false };
const tlsoptions = { ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false };
if (obj.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
const tlsock = new TLSSocket(ser, tlsoptions);
tlsock.on('error', function (err) { Debug(1, "CIRA TLS Connection Error ", err); });
tlsock.on('secureConnect', function () { Debug(2, "CIRA Secure TLS Connection"); ws._socket.resume(); });
@ -207,7 +208,8 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
obj.forwardclient.setEncoding('binary');
} else {
// If TLS is going to be used, setup a TLS socket
var tlsoptions = { secureProtocol: ((obj.tls1only == 1) ? 'TLSv1_method' : 'SSLv23_method'), ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false };
var tlsoptions = { ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false };
if (obj.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
obj.forwardclient = obj.tls.connect(port, node.host, tlsoptions, function () {
// The TLS connection method is the same as TCP, but located a bit differently.
Debug(2, 'TLS Intel AMT transport connected to ' + node.host + ':' + port + '.');

View File

@ -167,46 +167,15 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, parent,
obj.socketState = 1;
obj.kerberosDone = 0;
if (obj.mode==1 ) { //Direct
if (obj.xtls != 1) {
// Connect without TLS
obj.socket = new obj.net.Socket();
obj.socket.setEncoding('binary');
obj.socket.setTimeout(6000); // Set socket idle timeout
obj.socket.on('data', obj.xxOnSocketData);
obj.socket.on('close', obj.xxOnSocketClosed);
obj.socket.on('timeout', obj.xxOnSocketClosed);
obj.socket.connect(obj.port, obj.host, obj.xxOnSocketConnected);
} else {
// Connect with TLS
var options = { secureProtocol: ((obj.xtlsMethod == 0) ? 'SSLv23_method' : 'TLSv1_method'), 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.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;
obj.xtlsoptions = options;
}
obj.socket = obj.tls.connect(obj.port, obj.host, obj.xtlsoptions, obj.xxOnSocketConnected);
obj.socket.setEncoding('binary');
obj.socket.setTimeout(6000); // Set socket idle timeout
obj.socket.on('data', obj.xxOnSocketData);
obj.socket.on('close', obj.xxOnSocketClosed);
obj.socket.on('timeout', obj.xxOnSocketClosed);
obj.socket.on('error', function (e) { if (e.message && e.message.indexOf('sslv3 alert bad record mac') >= 0) { obj.xtlsMethod = 1 - obj.xtlsMethod; } });
}
obj.socket.setNoDelay(true); // Disable nagle. We will encode each WSMAN request as a single send block and want to send it at once. This may help Intel AMT handle pipelining?
} else if (obj.mode==2 || obj.mode==3) { // CIRA and APF
if (obj.mode==2) { // CIRA
if ((obj.parent != null) && (obj.mode === 2) || (obj.mode === 3)) { // CIRA and APF
if (obj.mode == 2) { // CIRA
var ciraconn = obj.parent.mpsserver.ciraConnections[obj.host];
obj.socket = obj.parent.mpsserver.SetupCiraChannel(ciraconn, obj.port);
} else { //APF
} else { // APF
var apfconn = obj.parent.apfserver.apfConnections[obj.host];
obj.socket = obj.parent.apfserver.SetupCiraChannel(apfconn, obj.port);
}
obj.socket.onData = function (ccon, data) {
obj.xxOnSocketData(data);
}
obj.socket.onData = function (ccon, data) { obj.xxOnSocketData(data); }
obj.socket.onStateChange = function (ccon, state) {
if (state == 0) {
try {
@ -222,6 +191,36 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, parent,
obj.xxOnSocketConnected();
}
}
} else {
// Direct connection
if (obj.xtls != 1) {
// Connect without TLS
obj.socket = new obj.net.Socket();
obj.socket.setEncoding('binary');
obj.socket.setTimeout(6000); // Set socket idle timeout
obj.socket.on('data', obj.xxOnSocketData);
obj.socket.on('close', obj.xxOnSocketClosed);
obj.socket.on('timeout', obj.xxOnSocketClosed);
obj.socket.connect(obj.port, obj.host, obj.xxOnSocketConnected);
} else {
// Connect with TLS
var options = { 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 != 0) { 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;
obj.xtlsoptions = options;
}
obj.socket = obj.tls.connect(obj.port, obj.host, obj.xtlsoptions, obj.xxOnSocketConnected);
obj.socket.setEncoding('binary');
obj.socket.setTimeout(6000); // Set socket idle timeout
obj.socket.on('data', obj.xxOnSocketData);
obj.socket.on('close', obj.xxOnSocketClosed);
obj.socket.on('timeout', obj.xxOnSocketClosed);
obj.socket.on('error', function (e) { if (e.message && e.message.indexOf('sslv3 alert bad record mac') >= 0) { obj.xtlsMethod = 1 - obj.xtlsMethod; } });
}
obj.socket.setNoDelay(true); // Disable nagle. We will encode each WSMAN request as a single send block and want to send it at once. This may help Intel AMT handle pipelining?
}
}

View File

@ -733,7 +733,7 @@ function AmtStackCreateService(wsmanStack) {
e = null;
try {
es = atob(responses.Body['EventRecords'][i]);
e = new Buffer(es);
e = Buffer.from(es);
} catch (ex) {
console.log(ex + " " + responses.Body['EventRecords'][i])
}

View File

@ -166,7 +166,7 @@ module.exports.CreateAmtScanner = function (parent) {
if (err == null && docs.length > 0) {
for (var i in docs) {
var doc = docs[i], host = doc.host.toLowerCase();
const ciraConnection = obj.parent.mpsserver.ciraConnections[doc._id];
const ciraConnection = obj.parent.mpsserver ? obj.parent.mpsserver.ciraConnections[doc._id] : null;
if ((host != '127.0.0.1') && (host != '::1') && (host.toLowerCase() != 'localhost') && (ciraConnection == null)) {
var scaninfo = obj.scanTable[doc._id];
if (scaninfo == undefined) {
@ -371,7 +371,9 @@ module.exports.CreateAmtScanner = function (parent) {
} else {
// Connect using TLS, we will switch from default TLS to TLS1-only and back if we get a connection error to support older Intel AMT.
if (scaninfo.tlsoption == null) { scaninfo.tlsoption = 0; }
client = obj.tls.connect(port, host, scaninfo.tlsoption == 1 ? { secureProtocol: 'TLSv1_method', rejectUnauthorized: false, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE } : { rejectUnauthorized: false, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE }, function () { this.write('GET / HTTP/1.1\r\nhost: ' + host + '\r\n\r\n'); });
const tlsOptions = { rejectUnauthorized: false, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE };
if (scaninfo.tlsoption == 1) { tlsOptions.secureProtocol = 'TLSv1_method'; }
client = obj.tls.connect(port, host, tlsOptions, function () { this.write('GET / HTTP/1.1\r\nhost: ' + host + '\r\n\r\n'); });
}
client.scaninfo = scaninfo;
client.func = func;

View File

@ -200,12 +200,17 @@ module.exports.CertificateOperations = function (parent) {
if (u.protocol == 'https:') {
// Read the certificate from HTTPS
if (hostname == null) { hostname = u.hostname; }
const tlssocket = obj.tls.connect((u.port ? u.port : 443), u.hostname, { servername: hostname, rejectUnauthorized: false }, function () { this.xxcert = this.getPeerCertificate(); this.end(); });
parent.debug('cert', "loadCertificate() - Loading certificate from " + u.hostname + ":" + (u.port ? u.port : 443) + ", Hostname: " + hostname + "...");
const tlssocket = obj.tls.connect((u.port ? u.port : 443), u.hostname, { servername: hostname, rejectUnauthorized: false }, function () {
this.xxcert = this.getPeerCertificate();
parent.debug('cert', "loadCertificate() - TLS connected, " + ((this.xxcert != null) ? "got certificate." : "no certificate."));
try { this.destroy(); } catch (ex) { }
this.xxfunc(this.xxurl, (this.xxcert == null)?null:(this.xxcert.raw.toString('binary')), hostname, this.xxtag);
});
tlssocket.xxurl = url;
tlssocket.xxfunc = func;
tlssocket.xxtag = tag;
tlssocket.on('end', function () { this.xxfunc(this.xxurl, this.xxcert.raw.toString('binary'), hostname, this.xxtag); });
tlssocket.on('error', function () { this.xxfunc(this.xxurl, null, hostname, this.xxtag); });
tlssocket.on('error', function (error) { try { this.destroy(); } catch (ex) { } parent.debug('cert', "loadCertificate() - TLS error: " + error); this.xxfunc(this.xxurl, null, hostname, this.xxtag); });
} else if (u.protocol == 'file:') {
// Read the certificate from a file
obj.fs.readFile(url.substring(7), 'utf8', function (err, data) {
@ -301,10 +306,8 @@ module.exports.CertificateOperations = function (parent) {
var cert = obj.pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = String(Math.floor((Math.random() * 100000) + 1));
cert.validity.notBefore = new Date();
cert.validity.notBefore.setFullYear(cert.validity.notBefore.getFullYear() - 1); // Create a certificate that is valid one year before, to make sure out-of-sync clocks don"t reject this cert.
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 30);
cert.validity.notBefore = new Date(2018, 0, 1);
cert.validity.notAfter = new Date(2049, 11, 31);
if (addThumbPrintToName === true) { commonName += '-' + obj.pki.getPublicKeyFingerprint(cert.publicKey, { encoding: 'hex' }).substring(0, 6); }
if (country == null) { country = "unknown"; }
if (organization == null) { organization = "unknown"; }
@ -325,10 +328,8 @@ module.exports.CertificateOperations = function (parent) {
var cert = obj.pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = String(Math.floor((Math.random() * 100000) + 1));
cert.validity.notBefore = new Date();
cert.validity.notBefore.setFullYear(cert.validity.notAfter.getFullYear() - 1); // Create a certificate that is valid one year before, to make sure out-of-sync clocks don"t reject this cert.
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 30);
cert.validity.notBefore = new Date(2018, 0, 1);
cert.validity.notAfter = new Date(2049, 11, 31);
if (addThumbPrintToName === true) { commonName += "-" + obj.pki.getPublicKeyFingerprint(cert.publicKey, { encoding: 'hex' }).substring(0, 6); }
var attrs = [{ name: 'commonName', value: commonName }];
if (country != null) { attrs.push({ name: 'countryName', value: country }); }
@ -807,7 +808,7 @@ module.exports.CertificateOperations = function (parent) {
accelerator.accid = acceleratorCreateCount;
accelerator.on('message', function (message) {
acceleratorMessage++;
this.x.func(this.x.tag, message);
if (this.x.func) { this.x.func(this.x.tag, message); }
delete this.x;
if (pendingAccelerator.length > 0) { this.send(this.x = pendingAccelerator.shift()); } else { freeAccelerators.push(this); }
});
@ -854,5 +855,24 @@ module.exports.CertificateOperations = function (parent) {
}
};
// Perform any general operation
obj.acceleratorPerformOperation = function (operation, data, tag, func) {
if (acceleratorTotalCount <= 1) {
// No accelerators available
require(program).processMessage({ action: operation, data: data, tag: tag, func: func });
} else {
var acc = obj.getAccelerator();
if (acc == null) {
// Add to pending accelerator workload
acceleratorPerformSignaturePushFuncCall++;
pendingAccelerator.push({ action: operation, data: data, tag: tag, func: func });
} else {
// Send to accelerator now
acceleratorPerformSignatureRunFuncCall++;
acc.send(acc.x = { action: operation, data: data, tag: tag, func: func });
}
}
};
return obj;
};

View File

@ -26,8 +26,8 @@ module.exports.ShortToStr = function (v) { return String.fromCharCode((v >> 8) &
module.exports.ShortToStrX = function (v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF); };
module.exports.IntToStr = function (v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); };
module.exports.IntToStrX = function (v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); };
module.exports.MakeToArray = function (v) { if (!v || v == null || typeof v == "object") return v; return [v]; };
module.exports.SplitArray = function (v) { return v.split(","); };
module.exports.MakeToArray = function (v) { if (!v || v == null || typeof v == 'object') return v; return [v]; };
module.exports.SplitArray = function (v) { return v.split(','); };
module.exports.Clone = function (v) { return JSON.parse(JSON.stringify(v)); };
module.exports.IsFilenameValid = (function () { var x1 = /^[^\\/:\*\?"<>\|]+$/, x2 = /^\./, x3 = /^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname) { return module.exports.validateString(fname, 1, 4096) && x1.test(fname) && !x2.test(fname) && !x3.test(fname) && (fname[0] != '.'); }; })();
module.exports.makeFilename = function (v) { return v.split('\\').join('').split('/').join('').split(':').join('').split('*').join('').split('?').join('').split('"').join('').split('<').join('').split('>').join('').split('|').join('').split(' ').join('').split('\'').join(''); }
@ -122,8 +122,8 @@ module.exports.parseNameValueList = function (list) {
// Compute the MD5 digest hash for a set of values
module.exports.ComputeDigesthash = function (username, password, realm, method, path, qop, nonce, nc, cnonce) {
var ha1 = crypto.createHash('md5').update(username + ":" + realm + ":" + password).digest("hex");
var ha2 = crypto.createHash('md5').update(method + ":" + path).digest("hex");
var ha1 = crypto.createHash('md5').update(username + ":" + realm + ":" + password).digest('hex');
var ha2 = crypto.createHash('md5').update(method + ":" + path).digest('hex');
return crypto.createHash('md5').update(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2).digest("hex");
};
@ -147,6 +147,7 @@ module.exports.escapeFieldName = function (name) { if ((name.indexOf('%') == -1)
module.exports.unEscapeFieldName = function (name) { if (name.indexOf('%') == -1) return name; return name.split('%2E').join('.').split('%24').join('$').split('%25').join('%'); };
// Escape all links
module.exports.escapeLinksFieldNameEx = function (docx) { if (docx.links == null) { return docx; } var doc = Object.assign({}, docx); doc.links = Object.assign({}, doc.links); for (var i in doc.links) { var ue = module.exports.escapeFieldName(i); if (ue !== i) { doc.links[ue] = doc.links[i]; delete doc.links[i]; } } return doc; };
module.exports.escapeLinksFieldName = function (docx) { var doc = Object.assign({}, docx); if (doc.links != null) { doc.links = Object.assign({}, doc.links); for (var i in doc.links) { var ue = module.exports.escapeFieldName(i); if (ue !== i) { doc.links[ue] = doc.links[i]; delete doc.links[i]; } } } return doc; };
module.exports.unEscapeLinksFieldName = function (doc) { if (doc.links != null) { for (var j in doc.links) { var ue = module.exports.unEscapeFieldName(j); if (ue !== j) { doc.links[ue] = doc.links[j]; delete doc.links[j]; } } } return doc; };
//module.exports.escapeAllLinksFieldName = function (docs) { for (var i in docs) { module.exports.escapeLinksFieldName(docs[i]); } return docs; };
@ -251,7 +252,7 @@ module.exports.translationsToJson = function(t) {
for (var i in arr) {
var names = [], el = arr[i], el2 = {};
for (var j in el) { names.push(j); }
names.sort();
names.sort(function (a, b) { if (a == b) { return 0; } if (a == 'xloc') { return 1; } if (b == 'xloc') { return -1; } return a - b });
for (var j in names) { el2[names[j]] = el[names[j]]; }
if (el2.xloc != null) { el2.xloc.sort(); }
arr2.push(el2);

121
db.js
View File

@ -28,9 +28,9 @@
module.exports.CreateDB = function (parent, func) {
var obj = {};
var Datastore = null;
var expireEventsSeconds = (60 * 60 * 24 * 20); // By default, expire events after 20 days. (Seconds * Minutes * Hours * Days)
var expirePowerEventsSeconds = (60 * 60 * 24 * 10); // By default, expire power events after 10 days. (Seconds * Minutes * Hours * Days)
var expireServerStatsSeconds = (60 * 60 * 24 * 30); // By default, expire power events after 30 days. (Seconds * Minutes * Hours * Days)
var expireEventsSeconds = (60 * 60 * 24 * 20); // By default, expire events after 20 days (1728000). (Seconds * Minutes * Hours * Days)
var expirePowerEventsSeconds = (60 * 60 * 24 * 10); // By default, expire power events after 10 days (864000). (Seconds * Minutes * Hours * Days)
var expireServerStatsSeconds = (60 * 60 * 24 * 30); // By default, expire power events after 30 days (2592000). (Seconds * Minutes * Hours * Days)
const common = require('./common.js');
obj.identifier = null;
obj.dbKey = null;
@ -42,7 +42,8 @@ module.exports.CreateDB = function (parent, func) {
// Check if the database unique identifier is present
// This is used to check that in server peering mode, everyone is using the same database.
obj.Get('DatabaseIdentifier', function (err, docs) {
if ((docs.length == 1) && (docs[0].value != null)) {
if (err != null) { parent.debug('db', 'ERROR (Get DatabaseIdentifier): ' + err); }
if ((err == null) && (docs.length == 1) && (docs[0].value != null)) {
obj.identifier = docs[0].value;
} else {
obj.identifier = Buffer.from(require('crypto').randomBytes(48), 'binary').toString('hex');
@ -52,8 +53,9 @@ module.exports.CreateDB = function (parent, func) {
// Load database schema version and check if we need to update
obj.Get('SchemaVersion', function (err, docs) {
if (err != null) { parent.debug('db', 'ERROR (Get SchemaVersion): ' + err); }
var ver = 0;
if (docs && docs.length == 1) { ver = docs[0].value; }
if ((err == null) && (docs.length == 1)) { ver = docs[0].value; }
if (ver == 1) { console.log('This is an unsupported beta 1 database, delete it to create a new one.'); process.exit(0); }
// TODO: Any schema upgrades here...
@ -87,6 +89,7 @@ module.exports.CreateDB = function (parent, func) {
// Remove all objects that have a "meshid" that no longer points to a valid mesh.
obj.GetAllType('mesh', function (err, docs) {
if (err != null) { parent.debug('db', 'ERROR (GetAll mesh): ' + err); }
var meshlist = [];
if ((err == null) && (docs.length > 0)) { for (var i in docs) { meshlist.push(docs[i]._id); } }
if ((obj.databaseType == 4) || (obj.databaseType == 5)) {
@ -102,7 +105,8 @@ module.exports.CreateDB = function (parent, func) {
// Fix all of the creating & login to ticks by seconds, not milliseconds.
obj.GetAllType('user', function (err, docs) {
if (err == null && docs.length > 0) {
if (err != null) { parent.debug('db', 'ERROR (GetAll user): ' + err); }
if ((err == null) && (docs.length > 0)) {
for (var i in docs) {
var fixed = false;
@ -182,14 +186,14 @@ module.exports.CreateDB = function (parent, func) {
// MongoDB
obj.file.aggregate([{ "$group": { _id: "$type", count: { $sum: 1 } } }]).toArray(function (err, docs) {
var counters = {}, totalCount = 0;
for (var i in docs) { if (docs[i]._id != null) { counters[docs[i]._id] = docs[i].count; totalCount += docs[i].count; } }
if (err == null) { for (var i in docs) { if (docs[i]._id != null) { counters[docs[i]._id] = docs[i].count; totalCount += docs[i].count; } } }
func(counters);
});
} else if (obj.databaseType == 2) {
// MongoJS
obj.file.aggregate([{ "$group": { _id: "$type", count: { $sum: 1 } } }], function (err, docs) {
var counters = {}, totalCount = 0;
for (var i in docs) { if (docs[i]._id != null) { counters[docs[i]._id] = docs[i].count; totalCount += docs[i].count; } }
if (err == null) { for (var i in docs) { if (docs[i]._id != null) { counters[docs[i]._id] = docs[i].count; totalCount += docs[i].count; } } }
func(counters);
});
} else if (obj.databaseType == 1) {
@ -219,13 +223,14 @@ module.exports.CreateDB = function (parent, func) {
}
// This is used to rate limit a number of operation per day. Returns a startValue each new days, but you can substract it and save the value in the db.
obj.getValueOfTheDay = function (id, startValue, func) { obj.Get(id, function (err, docs) { var date = new Date(), t = date.toLocaleDateString(); if (docs.length == 1) { var r = docs[0]; if (r.day == t) { func({ _id: id, value: r.value, day: t }); return; } } func({ _id: id, value: startValue, day: t }); }); };
obj.getValueOfTheDay = function (id, startValue, func) { obj.Get(id, function (err, docs) { var date = new Date(), t = date.toLocaleDateString(); if ((err == null) && (docs.length == 1)) { var r = docs[0]; if (r.day == t) { func({ _id: id, value: r.value, day: t }); return; } } func({ _id: id, value: startValue, day: t }); }); };
obj.escapeBase64 = function escapeBase64(val) { return (val.replace(/\+/g, '@').replace(/\//g, '$')); }
// Encrypt an database object
obj.performRecordEncryptionRecode = function (func) {
var count = 0;
obj.GetAllType('user', function (err, docs) {
if (err != null) { parent.debug('db', 'ERROR (performRecordEncryptionRecode): ' + err); }
if (err == null) { for (var i in docs) { count++; obj.Set(docs[i]); } }
obj.GetAllType('node', function (err, docs) {
if (err == null) { for (var i in docs) { count++; obj.Set(docs[i]); } }
@ -236,7 +241,7 @@ module.exports.CreateDB = function (parent, func) {
// Encrypt an database object
function performTypedRecordDecrypt(data) {
if ((obj.dbRecordsDecryptKey == null) || (typeof data != 'object')) return data;
if ((data == null) || (obj.dbRecordsDecryptKey == null) || (typeof data != 'object')) return data;
for (var i in data) {
if (data[i].type == 'user') {
data[i] = performPartialRecordDecrypt(data[i]);
@ -335,7 +340,9 @@ module.exports.CreateDB = function (parent, func) {
}
//sqlDbQuery('DROP DATABASE MeshCentral', null, function (err, docs) { console.log('DROP'); }); return;
sqlDbQuery('USE meshcentral', null, function (err, docs) {
if (err != null) { parent.debug('db', 'ERROR: USE meshcentral: ' + err); }
if (err == null) { setupFunctions(func); } else {
parent.debug('db', 'Creating database...');
sqlDbBatchExec([
'CREATE DATABASE meshcentral',
// Main table
@ -364,7 +371,10 @@ module.exports.CreateDB = function (parent, func) {
'CREATE INDEX ndxsmbiosexpire ON meshcentral.smbios (expire)',
// Plugins table
'CREATE TABLE meshcentral.plugin (id INT NOT NULL AUTO_INCREMENT, doc JSON, PRIMARY KEY(id), CHECK (json_valid(doc)))'
], function (err) { /*if (err != null) { console.log(err); }*/ setupFunctions(func); });
], function (err) {
if (err != null) { parent.debug('db', 'BatchSetupDb: ' + err); }
setupFunctions(func);
});
}
});
} else if (parent.args.mongodb) {
@ -373,6 +383,7 @@ module.exports.CreateDB = function (parent, func) {
require('mongodb').MongoClient.connect(parent.args.mongodb, { useNewUrlParser: true, useUnifiedTopology: true }, function (err, client) {
if (err != null) { console.log("Unable to connect to database: " + err); process.exit(); return; }
Datastore = client;
parent.debug('db', 'Connected to MongoDB database...');
// Get the database name and setup the database client
var dbname = 'meshcentral';
@ -778,15 +789,31 @@ module.exports.CreateDB = function (parent, func) {
// Database actions on the main collection (MariaDB or MySQL)
obj.Set = function (value, func) {
var extra = null, extraex = null;
value = common.escapeLinksFieldNameEx(value);
if (value.meshid) { extra = value.meshid; } else if (value.email) { extra = 'email/' + value.email; }
if ((value.type == 'node') && (value.intelamt != null) && (value.intelamt.uuid != null)) { extraex = 'uuid/' + value.intelamt.uuid; }
sqlDbQuery('REPLACE INTO meshcentral.main VALUE (?, ?, ?, ?, ?, ?)', [value._id, (value.type ? value.type : null), ((value.domain != null) ? value.domain : null), extra, extraex, JSON.stringify(performTypedRecordEncrypt(value))], func);
}
obj.Get = function (_id, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id = ?', [_id], func); }
obj.Get = function (_id, func) {
sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id = ?', [_id], function (err, docs) {
if ((docs != null) && (docs.length > 0) && (docs[0].links != null)) { docs[0] = common.unEscapeLinksFieldName(docs[0]); }
func(_id, func);
});
}
obj.GetAll = function (func) { sqlDbQuery('SELECT domain, doc FROM meshcentral.main', null, func); }
obj.GetHash = function (id, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id = ?', [id], func); }
obj.GetAllTypeNoTypeField = function (type, domain, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = ? AND domain = ?', [type, domain], function (err, docs) { for (var i in docs) { delete docs[i].type } func(err, docs); }); };
obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, domain, type, id, func) { if (id && (id != '')) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id = ? AND type = ? AND domain = ? AND extra IN (?)', [id, type, domain, meshes], function (err, docs) { for (var i in docs) { delete docs[i].type } func(err, docs); }); } else { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = ? AND domain = ? AND extra IN (?)', [type, domain, meshes], function (err, docs) { for (var i in docs) { delete docs[i].type } func(err, docs); }); } };
obj.GetAllTypeNoTypeField = function (type, domain, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = ? AND domain = ?', [type, domain], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, docs); }); };
obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, func) {
if (id && (id != '')) {
sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id = ? AND type = ? AND domain = ? AND extra IN (?)', [id, type, domain, meshes], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, docs); });
} else {
if (extrasids == null) {
sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = ? AND domain = ? AND extra IN (?)', [type, domain, meshes], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, docs); });
} else {
sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = ? AND domain = ? AND (extra IN (?) OR id IN (?))', [type, domain, meshes, extrasids], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, docs); });
}
}
};
obj.GetAllType = function (type, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = ?', [type], func); }
obj.GetAllIdsOfType = function (ids, domain, type, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id IN (?) AND domain = ? AND type = ?', [ids, domain, type], func); }
obj.GetUserWithEmail = function (domain, email, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE domain = ? AND extra = ?', [domain, 'email/' + email], func); }
@ -796,11 +823,11 @@ module.exports.CreateDB = function (parent, func) {
obj.RemoveAllOfType = function (type, func) { sqlDbQuery('DELETE FROM meshcentral.main WHERE type = ?', [type], func); };
obj.InsertMany = function (data, func) { var pendingOps = 0; for (var i in data) { pendingOps++; obj.Set(data[i], function () { if (--pendingOps == 0) { func(); } }); } };
obj.RemoveMeshDocuments = function (id) { sqlDbQuery('DELETE FROM meshcentral.main WHERE extra = ?', [id], function () { sqlDbQuery('DELETE FROM meshcentral.main WHERE id = ?', ['nt' + id], func); } ); };
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if (docs.length == 1) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if ((err == null) && (docs.length == 1)) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
obj.DeleteDomain = function (domain, func) { sqlDbQuery('DELETE FROM meshcentral.main WHERE domain = ?', [domain], func); };
obj.SetUser = function (user) { if (user.subscriptions != null) { var u = Clone(user); if (u.subscriptions) { delete u.subscriptions; } obj.Set(u); } else { obj.Set(user); } };
obj.dispose = function () { for (var x in obj) { if (obj[x].close) { obj[x].close(); } delete obj[x]; } };
obj.getLocalAmtNodes = function (func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE (type = "node") AND (extraex IS NOT NULL)', null, function (err, docs) { var r = []; for (var i in docs) { if (docs[i].host != null) { r.push(docs[i]); } } func(err, r); }); };
obj.getLocalAmtNodes = function (func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE (type = "node") AND (extraex IS NOT NULL)', null, function (err, docs) { var r = []; if (err == null) { for (var i in docs) { if (docs[i].host != null) { r.push(docs[i]); } } } func(err, r); }); };
obj.getAmtUuidMeshNode = function (meshid, uuid, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE meshid = ? AND extraex = ?', [meshid, 'uuid/' + uuid], func); };
obj.getAmtUuidNode = function (uuid, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = "node" AND extraex = ?', ['uuid/' + uuid], func); };
obj.isMaxType = function (max, type, domainid, func) { if (max == null) { func(false); } else { sqlDbExec('SELECT COUNT(id) FROM meshcentral.main WHERE domain = ? AND type = ?', [domainid, type], function (err, response) { func((response['COUNT(id)'] == null) || (response['COUNT(id)'] > max), response['COUNT(id)']) }); } }
@ -904,12 +931,12 @@ module.exports.CreateDB = function (parent, func) {
obj.getPlugins = function (func) { sqlDbQuery('SELECT doc FROM meshcentral.plugin', null, func); }; // Get all plugins
obj.getPlugin = function (id, func) { sqlDbQuery('SELECT doc FROM meshcentral.plugin WHERE id = ?', [id], func); }; // Get plugin
obj.deletePlugin = function (id, func) { sqlDbQuery('DELETE FROM meshcentral.plugin WHERE id = ?', [id], func); }; // Delete plugin
obj.setPluginStatus = function (id, status, func) { obj.getPlugin(id, function (err, docs) { if (docs.length == 1) { docs[0].status = status; obj.updatePlugin(id, docs[0], func); } }); };
obj.setPluginStatus = function (id, status, func) { obj.getPlugin(id, function (err, docs) { if ((err == null) && (docs.length == 1)) { docs[0].status = status; obj.updatePlugin(id, docs[0], func); } }); };
obj.updatePlugin = function (id, args, func) { delete args._id; sqlDbQuery('REPLACE INTO meshcentral.plugin VALUE (?, ?)', [id, JSON.stringify(args)], func); };
}
} else if (obj.databaseType == 3) {
// Database actions on the main collection (MongoDB)
obj.Set = function (data, func) { obj.file.replaceOne({ _id: data._id }, performTypedRecordEncrypt(data), { upsert: true }, func); };
obj.Set = function (data, func) { 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];
@ -921,15 +948,31 @@ module.exports.CreateDB = function (parent, func) {
userCallback.apply(obj, _func2.userArgs);
};
func2.userArgs = parms;
obj.file.find({ _id: id }).toArray(function (err, docs) { func2(err, performTypedRecordDecrypt(docs)); });
obj.file.find({ _id: id }).toArray(function (err, docs) {
if ((docs != null) && (docs.length > 0) && (docs[0].links != null)) { docs[0] = common.unEscapeLinksFieldName(docs[0]); }
func2(err, performTypedRecordDecrypt(docs));
});
} else {
obj.file.find({ _id: id }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
obj.file.find({ _id: id }).toArray(function (err, docs) {
if ((docs != null) && (docs.length > 0) && (docs[0].links != null)) { docs[0] = common.unEscapeLinksFieldName(docs[0]); }
func(err, performTypedRecordDecrypt(docs));
});
}
};
obj.GetAll = function (func) { obj.file.find({}).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetHash = function (id, func) { obj.file.find({ _id: id }).project({ _id: 0, hash: 1 }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetAllTypeNoTypeField = function (type, domain, func) { obj.file.find({ type: type, domain: domain }).project({ type: 0 }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, domain, type, id, func) { var x = { type: type, domain: domain, meshid: { $in: meshes } }; if (id) { x._id = id; } obj.file.find(x, { type: 0 }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, func) {
if (extrasids == null) {
var x = { type: type, domain: domain, meshid: { $in: meshes } };
if (id) { x._id = id; }
obj.file.find(x, { type: 0 }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
} else {
var x = { type: type, domain: domain, $or: [ { meshid: { $in: meshes } }, { _id: { $in: extrasids } } ] };
if (id) { x._id = id; }
obj.file.find(x, { type: 0 }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
}
};
obj.GetAllType = function (type, func) { obj.file.find({ type: type }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetAllIdsOfType = function (ids, domain, type, func) { obj.file.find({ type: type, domain: domain, _id: { $in: ids } }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetUserWithEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email }).project({ type: 0 }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
@ -939,7 +982,7 @@ module.exports.CreateDB = function (parent, func) {
obj.RemoveAllOfType = function (type, func) { obj.file.deleteMany({ type: type }, { multi: true }, func); };
obj.InsertMany = function (data, func) { obj.file.insertMany(data, func); };
obj.RemoveMeshDocuments = function (id) { obj.file.deleteMany({ meshid: id }, { multi: true }); obj.file.deleteOne({ _id: 'nt' + id }); };
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if (docs.length == 1) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if ((err == null) && (docs.length == 1)) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
obj.DeleteDomain = function (domain, func) { obj.file.deleteMany({ domain: domain }, { multi: true }, func); };
obj.SetUser = function (user) { if (user.subscriptions != null) { var u = Clone(user); if (u.subscriptions) { delete u.subscriptions; } obj.Set(u); } else { obj.Set(user); } };
obj.dispose = function () { for (var x in obj) { if (obj[x].close) { obj[x].close(); } delete obj[x]; } };
@ -1041,7 +1084,7 @@ module.exports.CreateDB = function (parent, func) {
} else {
// Database actions on the main collection (NeDB and MongoJS)
obj.Set = function (data, func) { var xdata = performTypedRecordEncrypt(data); obj.file.update({ _id: xdata._id }, xdata, { upsert: true }, func); };
obj.Set = function (data, func) { 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];
@ -1053,15 +1096,36 @@ module.exports.CreateDB = function (parent, func) {
userCallback.apply(obj, _func2.userArgs);
};
func2.userArgs = parms;
obj.file.find({ _id: id }, function (err, docs) { func2(err, performTypedRecordDecrypt(docs)); });
obj.file.find({ _id: id }, function (err, docs) {
if ((docs != null) && (docs.length > 0) && (docs[0].links != null)) { docs[0] = common.unEscapeLinksFieldName(docs[0]); }
func2(err, performTypedRecordDecrypt(docs));
});
} else {
obj.file.find({ _id: id }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
obj.file.find({ _id: id }, function (err, docs) {
if ((docs != null) && (docs.length > 0) && (docs[0].links != null)) { docs[0] = common.unEscapeLinksFieldName(docs[0]); }
func(err, performTypedRecordDecrypt(docs));
});
}
};
obj.GetAll = function (func) { obj.file.find({}, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetHash = function (id, func) { obj.file.find({ _id: id }, { _id: 0, hash: 1 }, func); };
obj.GetAllTypeNoTypeField = function (type, domain, func) { obj.file.find({ type: type, domain: domain }, { type: 0 }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, domain, type, id, func) { var x = { type: type, domain: domain, meshid: { $in: meshes } }; if (id) { x._id = id; } obj.file.find(x, { type: 0 }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
//obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, domain, type, id, func) {
//var x = { type: type, domain: domain, meshid: { $in: meshes } };
//if (id) { x._id = id; }
//obj.file.find(x, { type: 0 }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
//};
obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, extrasids, domain, type, id, func) {
if (extrasids == null) {
var x = { type: type, domain: domain, meshid: { $in: meshes } };
if (id) { x._id = id; }
obj.file.find(x, { type: 0 }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
} else {
var x = { type: type, domain: domain, $or: [{ meshid: { $in: meshes } }, { _id: { $in: extrasids } }] };
if (id) { x._id = id; }
obj.file.find(x, { type: 0 }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); });
}
};
obj.GetAllType = function (type, func) { obj.file.find({ type: type }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetAllIdsOfType = function (ids, domain, type, func) { obj.file.find({ type: type, domain: domain, _id: { $in: ids } }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
obj.GetUserWithEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email }, { type: 0 }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); };
@ -1071,7 +1135,7 @@ module.exports.CreateDB = function (parent, func) {
obj.RemoveAllOfType = function (type, func) { obj.file.remove({ type: type }, { multi: true }, func); };
obj.InsertMany = function (data, func) { obj.file.insert(data, func); };
obj.RemoveMeshDocuments = function (id) { obj.file.remove({ meshid: id }, { multi: true }); obj.file.remove({ _id: 'nt' + id }); };
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if (docs.length == 1) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if ((err == null) && (docs.length == 1)) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
obj.DeleteDomain = function (domain, func) { obj.file.remove({ domain: domain }, { multi: true }, func); };
obj.SetUser = function (user) { if (user.subscriptions != null) { var u = Clone(user); if (u.subscriptions) { delete u.subscriptions; } obj.Set(u); } else { obj.Set(user); } };
obj.dispose = function () { for (var x in obj) { if (obj[x].close) { obj[x].close(); } delete obj[x]; } };
@ -1236,7 +1300,7 @@ module.exports.CreateDB = function (parent, func) {
var mongoDumpPath = 'mongodump';
if (parent.config.settings.autobackup && parent.config.settings.autobackup.mongodumppath) { mongoDumpPath = parent.config.settings.autobackup.mongodumppath; }
const child_process = require('child_process');
const cmd = '\"' + mongoDumpPath + '\" --db=\"' + dbname + '\" --archive=\"' + newBackupPath + '.archive\"';
var cmd = '\"' + mongoDumpPath + '\" --db=\"' + dbname + '\" --archive=\"' + newBackupPath + '.archive\"';
if (dburl) { cmd = '\"' + mongoDumpPath + '\" --uri=\"' + dburl + '\" --archive=\"' + newBackupPath + '.archive\"'; }
var backupProcess = child_process.exec(cmd, { cwd: backupPath }, function (error, stdout, stderr) {
try {
@ -1312,6 +1376,7 @@ module.exports.CreateDB = function (parent, func) {
// Called when a node has changed
function dbNodeChange(nodeChange, added) {
common.unEscapeLinksFieldName(nodeChange.fullDocument);
const node = nodeChange.fullDocument;
if (node.intelamt && node.intelamt.pass) { delete node.intelamt.pass; } // Remove the Intel AMT password before eventing this.
parent.DispatchEvent(['*', node.meshid], obj, { etype: 'node', action: (added ? 'addnode' : 'changenode'), node: node, nodeid: node._id, domain: node.domain, nolog: 1 });

15
emails/account-check.html Normal file
View File

@ -0,0 +1,15 @@
<div>[[[SERVERNAME]]] - Email Verification</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8>
<tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b>
</td>
</tr>
</table>
<p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting email verification, click on the following link to complete the process.</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Click here to verify your e-mail address.</a>
</p>
If you did not initiate this request, please ignore this mail.
</div>

6
emails/account-check.txt Normal file
View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]] - Email Verification
Hi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is performing an e-mail verification. Nagivate to the following link to complete the process:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
If you did not initiate this request, please ignore this mail.

View File

@ -0,0 +1,19 @@
<div>[[[SERVERNAME]]] - Account Invitation</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8>
<tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Account Invitation</b>
</td>
</tr>
</table>
<p>An account was created for you on server <a href="[[[SERVERURL]]]" notrans="1">[[[SERVERNAME]]]</a>, you can access it now with:</p>
<p>
&nbsp;&nbsp;&nbsp;Username: <b notrans="1">[[[ACCOUNTNAME]]]</b><br />
&nbsp;&nbsp;&nbsp;Password: <b notrans="1">[[[PASSWORD]]]</b>
</p>
Best regards,
<br />
[[[USERNAME]]]
<br />
</div>

View File

@ -0,0 +1,5 @@
[[[SERVERNAME]]] - Account Invitation
An account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username "[[[ACCOUNTNAME]]]" and password "[[[PASSWORD]]]".
~
Best regards,
~[[[USERNAME]]]

12
emails/account-login.html Normal file
View File

@ -0,0 +1,12 @@
<div>[[[SERVERNAME]]] - Account Login</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8>
<tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Account Login</b>
</td>
</tr>
</table>
<p>Your login token is: [[[TOKEN]]]</p>
<p>This token can only be used once and is valid for 5 minutes.</p>
</div>

4
emails/account-login.txt Normal file
View File

@ -0,0 +1,4 @@
[[[SERVERNAME]]] - Account Login
Your login token is: [[[TOKEN]]]
~
This token can only be used once and is valid for 5 minutes.

15
emails/account-reset.html Normal file
View File

@ -0,0 +1,15 @@
<div>[[[SERVERNAME]]] - Account Reset</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8>
<tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b>
</td>
</tr>
</table>
<p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting an account password reset, click on the following link to complete the process.</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Click here to reset your account password.</a>
</p>
If you did not initiate this request, please ignore this mail.
</div>

6
emails/account-reset.txt Normal file
View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]] - Account Reset
Hi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is requesting an account password reset. Nagivate to the following link to complete the process:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
If you did not initiate this request, please ignore this mail.

42
emails/mesh-invite.html Normal file
View File

@ -0,0 +1,42 @@
<div>[[[SERVERNAME]]] - Invitation</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8>
<tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Agent Installation</b>
</td>
</tr>
</table>
<area-name>
<p>
Hello [[[NAME]]],
</p>
</area-name>
<p>User [[[USERNAME]]] on server <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting you to install software to start a remote control session.</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=3&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Click here to download the MeshAgent for Windows.</a>
</p>
</area-windows>
<area-osx>
<p style="margin-left:30px"><a href="[[[SERVERURL]]]/meshagents?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]">Click here to download the MeshAgent for Apple OSX.</a></p>
</area-osx>
<area-linux>
<p>
For Linux, cut & paste the following in a terminal to install the agent:<br />
<pre style="margin-left:30px" notrans="1">wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre>
</p>
</area-linux>
<area-link>
<p>
To install the software, <a href="[[[SERVERURL]]][[[LINKURL]]]">click here</a> and follow the instructions.
</p>
</area-link>
<p>If you did not initiate this request, please ignore this mail.</p>
Best regards,<br>[[[USERNAME]]]<br>
</div>

35
emails/mesh-invite.txt Normal file
View File

@ -0,0 +1,35 @@
[[[SERVERNAME]]] - Invitation
~<area-name>
Hello [[[NAME]]],
~</area-name>
User [[[USERNAME]]] on server [[[SERVERNAME]]] ([[[SERVERURL]]]/) is requesting you install software to start the remote control session.
~<area-msg>
~
Message: [[[MSG]]]
~
~</area-msg>
~<area-windows>
For Windows, nagivate to the following link to complete the process:
~
~[[[SERVERURL]]]/meshagents?id=3&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]
~
~</area-windows>
~<area-osx>
For Apple OSX, nagivate to the following link to complete the process:
~
~[[[SERVERURL]]]/meshagents?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]
~
~</area-osx>
~<area-linux>
For Linux, cut & paste the following in a terminal to install the agent:
~
~wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] '[[[MESHIDHEX]]]'
~
~</area-linux>
~<area-link>
To install the software, navigate to [[[SERVERURL]]][[[LINKURL]]] and follow the instructions.
~</area-link>
If you did not initiate this request, please ignore this mail.
~
Best regards,
~[[[USERNAME]]]

View File

@ -0,0 +1,15 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Ověření e-mailem</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Ověření</b>
</td>
</tr>
</tbody></table>
<p>Ahoj [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> požaduje ověření e-mailem a dokončete proces kliknutím na následující odkaz.</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Kliknutím sem ověřte svou e-mailovou adresu.</a>
</p>
Pokud jste tento požadavek nezačali, ignorujte tento e-mail.
</div></body></html>

View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]] - Ověření e-mailem
Ahoj [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) provádí ověření e-mailem. Chcete-li proces dokončit, přejděte na následující odkaz:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
Pokud jste tento požadavek nezačali, ignorujte tento e-mail.

View File

@ -0,0 +1,15 @@
<html><head></head><body><div>[[[SERVERNAME]]] - E-Mail-Überprüfung</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Überprüfung</b>
</td>
</tr>
</tbody></table>
<p>Hallo [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> Wenn Sie eine E-Mail-Bestätigung anfordern, klicken Sie auf den folgenden Link, um den Vorgang abzuschließen.</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">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></body></html>

View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]] - E-Mail-Überprüfung
Hallo [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) führt eine E-Mail-Überprüfung durch. Klicken Sie auf den folgenden Link, um den Vorgang abzuschließen:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
Wenn Sie diese Anfrage nicht initiiert haben, ignorieren Sie diese Mail bitte.

View File

@ -0,0 +1,15 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Email Verification</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b>
</td>
</tr>
</tbody></table>
<p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting email verification, click on the following link to complete the process.</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Click here to verify your e-mail address.</a>
</p>
If you did not initiate this request, please ignore this mail.
</div></body></html>

View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]] - Email Verification
Hi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is performing an e-mail verification. Nagivate to the following link to complete the process:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
If you did not initiate this request, please ignore this mail.

View File

@ -0,0 +1,15 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Vérification E-mail</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Vérification</b>
</td>
</tr>
</tbody></table>
<p>Bonjour [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> demande une vérification par e-mail, cliquez sur le lien suivant pour terminer le processus.</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Cliquez ici pour vérifier votre adresse e-mail.</a>
</p>
Si vous n'avez pas initié cette demande, veuillez ignorer ce courrier.
</div></body></html>

View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]] - Vérification E-mail
Bonjour [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) effectue une vérification par e-mail. Accédez au lien suivant pour terminer le processus:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
Si vous n'avez pas initié cette demande, veuillez ignorer ce courrier.

View File

@ -0,0 +1,15 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Email Verification</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b>
</td>
</tr>
</tbody></table>
<p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting email verification, click on the following link to complete the process.</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Click here to verify your e-mail address.</a>
</p>
If you did not initiate this request, please ignore this mail.
</div></body></html>

View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]] - Email Verification
Hi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is performing an e-mail verification. Nagivate to the following link to complete the process:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
If you did not initiate this request, please ignore this mail.

View File

@ -0,0 +1,15 @@
<html><head></head><body><div>[[[SERVERNAME]]]-メールの確認</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]]-確認</b>
</td>
</tr>
</tbody></table>
<p>[[[USERNAME]]]様 <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> がメールの確認をリクエストしている場合は、次のリンクをクリックしてプロセスを完了してください。</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">ここをクリックして、電子メールアドレスを確認してください。</a>
</p>
このリクエストを開始していない場合は、このメールを無視してください。
</div></body></html>

View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]]-メールの確認
こんにちは[[[USERNAME]]]、[[[SERVERNAME]]][[[SERVERURL]]])は電子メールの検証を実行しています。プロセスを完了するには、次のリンクに移動します。
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
このリクエストを開始していない場合は、このメールを無視してください。

View File

@ -0,0 +1,15 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Email Verification</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b>
</td>
</tr>
</tbody></table>
<p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting email verification, click on the following link to complete the process.</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Click here to verify your e-mail address.</a>
</p>
If you did not initiate this request, please ignore this mail.
</div></body></html>

View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]] - Email Verification
Hi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is performing an e-mail verification. Nagivate to the following link to complete the process:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
If you did not initiate this request, please ignore this mail.

View File

@ -0,0 +1,15 @@
<html><head></head><body><div>[[[SERVERNAME]]] - E-mail Verificatie</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verificatie</b>
</td>
</tr>
</tbody></table>
<p>Hallo [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> vraagt om e-mailverificatie, klik op de volgende link om het proces te voltooien.</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">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></body></html>

View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]] - E-mail Verificatie
Hallo [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) voert een e-mailverificatie uit. Ga naar de volgende link om het proces te voltooien:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
Als u dit verzoek niet heeft ingediend, dan kunt u deze e-mail negeren.

View File

@ -0,0 +1,15 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Email Verification</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b>
</td>
</tr>
</tbody></table>
<p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting email verification, click on the following link to complete the process.</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Click here to verify your e-mail address.</a>
</p>
If you did not initiate this request, please ignore this mail.
</div></body></html>

View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]] - Email Verification
Hi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is performing an e-mail verification. Nagivate to the following link to complete the process:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
If you did not initiate this request, please ignore this mail.

View File

@ -0,0 +1,15 @@
<html><head></head><body><div>[[[SERVERNAME]]] - подтверждение по электронной почте</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Подтверждение</b>
</td>
</tr>
</tbody></table>
<p>Привет [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> запрашивает подтверждение по электронной почте, нажмите на следующую ссылку, чтобы завершить процесс.</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Нажмите здесь, чтобы подтвердить свой адрес электронной почты.</a>
</p>
Если вы не инициировали этот запрос, игнорируйте это письмо.
</div></body></html>

View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]] - подтверждение по электронной почте
Здравствуйте, [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) выполняет проверку электронной почты. Для завершения процесса перейдите по следующей ссылке:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
Если вы не инициировали этот запрос, игнорируйте это письмо.

View File

@ -0,0 +1,15 @@
<html><head></head><body><div>[[[SERVERNAME]]]-电子邮件验证</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]]-验证</b>
</td>
</tr>
</tbody></table>
<p>[[[USERNAME]],你好, <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> 正在请求电子邮件验证,请单击以下链接以完成该过程。</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">单击此处以验证您的电子邮件地址。</a>
</p>
如果您没有发起此请求,请忽略此邮件。
</div></body></html>

View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]]-电子邮件验证
嗨[[[USERNAME]]][[[SERVERNAME]]][[[SERVERURL]]])正在执行电子邮件验证。导航至以下链接以完成该过程:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
如果您没有发起此请求,请忽略此邮件。

View File

@ -0,0 +1,19 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Pozvánka na účet</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Pozvánka na účet</b>
</td>
</tr>
</tbody></table>
<p>Účet byl pro vás vytvořen na serveru <a href="[[[SERVERURL]]]" notrans="1">[[[SERVERNAME]]]</a>, nyní k němu máte přístup:</p>
<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></body></html>

View File

@ -0,0 +1,5 @@
[[[SERVERNAME]]] - Pozvánka na účet
An account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username "[[[ACCOUNTNAME]]]" and password "[[[PASSWORD]]]".
~
S pozdravem,
~[[[USERNAME]]]

View File

@ -0,0 +1,19 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Kontoeinladung</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Kontoeinladung</b>
</td>
</tr>
</tbody></table>
<p>Auf dem Server wurde ein Konto für Sie erstellt <a href="[[[SERVERURL]]]" notrans="1">[[[SERVERNAME]]]</a>können Sie jetzt darauf zugreifen mit:</p>
<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></body></html>

View File

@ -0,0 +1,5 @@
[[[SERVERNAME]]] - Kontoeinladung
An account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username "[[[ACCOUNTNAME]]]" and password "[[[PASSWORD]]]".
~
Freundliche Grüße,
~[[[USERNAME]]]

View File

@ -0,0 +1,19 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Account Invitation</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Account Invitation</b>
</td>
</tr>
</tbody></table>
<p>An account was created for you on server <a href="[[[SERVERURL]]]" notrans="1">[[[SERVERNAME]]]</a>, you can access it now with:</p>
<p>
&nbsp;&nbsp;&nbsp;Usuario: <b notrans="1">[[[ACCOUNTNAME]]]</b><br>
&nbsp;&nbsp;&nbsp;Contraseña: <b notrans="1">[[[PASSWORD]]]</b>
</p>
Best regards,
<br>
[[[USERNAME]]]
<br>
</div></body></html>

View File

@ -0,0 +1,5 @@
[[[SERVERNAME]]] - Account Invitation
An account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username "[[[ACCOUNTNAME]]]" and password "[[[PASSWORD]]]".
~
Best regards,
~[[[USERNAME]]]

View File

@ -0,0 +1,19 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Invitation au compte</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Invitation au compte</b>
</td>
</tr>
</tbody></table>
<p>Un compte a été créé pour vous sur le serveur <a href="[[[SERVERURL]]]" notrans="1">[[[SERVERNAME]]]</a>, vous pouvez y accéder maintenant avec:</p>
<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></body></html>

View File

@ -0,0 +1,5 @@
[[[SERVERNAME]]] - Invitation au compte
An account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username "[[[ACCOUNTNAME]]]" and password "[[[PASSWORD]]]".
~
Meilleures salutations,
~[[[USERNAME]]]

View File

@ -0,0 +1,19 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Account Invitation</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Account Invitation</b>
</td>
</tr>
</tbody></table>
<p>An account was created for you on server <a href="[[[SERVERURL]]]" notrans="1">[[[SERVERNAME]]]</a>, you can access it now with:</p>
<p>
&nbsp;&nbsp;&nbsp;उपयोगकर्ता नाम: <b notrans="1">[[[ACCOUNTNAME]]]</b><br>
&nbsp;&nbsp;&nbsp;कुंजिका: <b notrans="1">[[[PASSWORD]]]</b>
</p>
Best regards,
<br>
[[[USERNAME]]]
<br>
</div></body></html>

View File

@ -0,0 +1,5 @@
[[[SERVERNAME]]] - Account Invitation
An account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username "[[[ACCOUNTNAME]]]" and password "[[[PASSWORD]]]".
~
Best regards,
~[[[USERNAME]]]

View File

@ -0,0 +1,19 @@
<html><head></head><body><div>[[[SERVERNAME]]]-アカウントの招待</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]]-アカウントの招待</b>
</td>
</tr>
</tbody></table>
<p>サーバー上にアカウントが作成されました <a href="[[[SERVERURL]]]" notrans="1">[[[SERVERNAME]]]</a>、あなたは今それを使ってそれにアクセスできます:</p>
<p>
&nbsp;&nbsp;&nbsp;ユーザー名: <b notrans="1">[[[ACCOUNTNAME]]]</b><br>
&nbsp;&nbsp;&nbsp;パスワード: <b notrans="1">[[[PASSWORD]]]</b>
</p>
宜しくお願いします、
<br>
[[[USERNAME]]]
<br>
</div></body></html>

View File

@ -0,0 +1,5 @@
[[[SERVERNAME]]]-アカウントの招待
An account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username "[[[ACCOUNTNAME]]]" and password "[[[PASSWORD]]]".
~
宜しくお願いします、
~[[[USERNAME]]]

View File

@ -0,0 +1,19 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Account Invitation</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Account Invitation</b>
</td>
</tr>
</tbody></table>
<p>An account was created for you on server <a href="[[[SERVERURL]]]" notrans="1">[[[SERVERNAME]]]</a>, you can access it now with:</p>
<p>
&nbsp;&nbsp;&nbsp;사용자 이름: <b notrans="1">[[[ACCOUNTNAME]]]</b><br>
&nbsp;&nbsp;&nbsp;암호: <b notrans="1">[[[PASSWORD]]]</b>
</p>
Best regards,
<br>
[[[USERNAME]]]
<br>
</div></body></html>

View File

@ -0,0 +1,5 @@
[[[SERVERNAME]]] - Account Invitation
An account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username "[[[ACCOUNTNAME]]]" and password "[[[PASSWORD]]]".
~
Best regards,
~[[[USERNAME]]]

View File

@ -0,0 +1,19 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Account uitnodiging</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Account uitnodiging</b>
</td>
</tr>
</tbody></table>
<p>Er is een account voor je aangemaakt op de server <a href="[[[SERVERURL]]]" notrans="1">[[[SERVERNAME]]]</a>, je hebt er nu toegang toe met:</p>
<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></body></html>

View File

@ -0,0 +1,5 @@
[[[SERVERNAME]]] - Account uitnodiging
Er is een account jouw aangemaakt op de server [[[SERVERNAME]]] ([[[SERVERURL]]]/), Je kan inloggen met de gebruikersnaam "[[[ACCOUNTNAME]]]" en wachtwoord "[[[PASSWORD]]]".
~
Vriendelijke groeten,
~[[[USERNAME]]]

View File

@ -0,0 +1,19 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Account Invitation</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Account Invitation</b>
</td>
</tr>
</tbody></table>
<p>An account was created for you on server <a href="[[[SERVERURL]]]" notrans="1">[[[SERVERNAME]]]</a>, you can access it now with:</p>
<p>
&nbsp;&nbsp;&nbsp;Nome de usuário: <b notrans="1">[[[ACCOUNTNAME]]]</b><br>
&nbsp;&nbsp;&nbsp;Senha: <b notrans="1">[[[PASSWORD]]]</b>
</p>
Best regards,
<br>
[[[USERNAME]]]
<br>
</div></body></html>

View File

@ -0,0 +1,5 @@
[[[SERVERNAME]]] - Account Invitation
An account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username "[[[ACCOUNTNAME]]]" and password "[[[PASSWORD]]]".
~
Best regards,
~[[[USERNAME]]]

View File

@ -0,0 +1,19 @@
<html><head></head><body><div>[[[SERVERNAME]]] - приглашение в аккаунт</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - приглашение в аккаунт</b>
</td>
</tr>
</tbody></table>
<p>Учетная запись была создана для вас на сервере <a href="[[[SERVERURL]]]" notrans="1">[[[SERVERNAME]]]</a>Вы можете получить к нему доступ сейчас:</p>
<p>
&nbsp;&nbsp;&nbsp;Имя пользователя: <b notrans="1">[[[ACCOUNTNAME]]]</b><br>
&nbsp;&nbsp;&nbsp;Пароль: <b notrans="1">[[[PASSWORD]]]</b>
</p>
С уважением,
<br>
[[[USERNAME]]]
<br>
</div></body></html>

View File

@ -0,0 +1,5 @@
[[[SERVERNAME]]] - приглашение в аккаунт
An account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username "[[[ACCOUNTNAME]]]" and password "[[[PASSWORD]]]".
~
С уважением,
~[[[USERNAME]]]

View File

@ -0,0 +1,19 @@
<html><head></head><body><div>[[[SERVERNAME]]]-帐户邀请</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]]-帐户邀请</b>
</td>
</tr>
</tbody></table>
<p>在服务器上为您创建了一个帐户 <a href="[[[SERVERURL]]]" notrans="1">[[[SERVERNAME]]]</a>,您现在可以通过以下方式访问它:</p>
<p>
&nbsp;&nbsp;&nbsp;用戶名: <b notrans="1">[[[ACCOUNTNAME]]]</b><br>
&nbsp;&nbsp;&nbsp;密碼: <b notrans="1">[[[PASSWORD]]]</b>
</p>
最好的祝福,
<br>
[[[USERNAME]]]
<br>
</div></body></html>

View File

@ -0,0 +1,5 @@
[[[SERVERNAME]]]-帐户邀请
An account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username "[[[ACCOUNTNAME]]]" and password "[[[PASSWORD]]]".
~
最好的祝福,
~[[[USERNAME]]]

View File

@ -0,0 +1,12 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Přihlášení k účtu</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Přihlášení k účtu</b>
</td>
</tr>
</tbody></table>
<p>Váš přihlašovací token je: [[[TOKEN]]]</p>
<p>Tento token lze použít pouze jednou a je platný po dobu 5 minut.</p>
</div></body></html>

View File

@ -0,0 +1,4 @@
[[[SERVERNAME]]] - Přihlášení k účtu
Váš přihlašovací token je: [[[TOKEN]]]
~
Tento token lze použít pouze jednou a je platný po dobu 5 minut.

View File

@ -0,0 +1,12 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Konto-Login</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Konto-Login</b>
</td>
</tr>
</tbody></table>
<p>Ihr Login-Token lautet: [[[TOKEN]]]</p>
<p>Dieser Token kann nur einmal verwendet werden und ist 5 Minuten gültig.</p>
</div></body></html>

View File

@ -0,0 +1,4 @@
[[[SERVERNAME]]] - Konto-Login
Ihr Login-Token lautet: [[[TOKEN]]]
~
Dieser Token kann nur einmal verwendet werden und ist 5 Minuten gültig.

View File

@ -0,0 +1,12 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Account Login</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Account Login</b>
</td>
</tr>
</tbody></table>
<p>Your login token is: [[[TOKEN]]]</p>
<p>This token can only be used once and is valid for 5 minutes.</p>
</div></body></html>

View File

@ -0,0 +1,4 @@
[[[SERVERNAME]]] - Account Login
Your login token is: [[[TOKEN]]]
~
This token can only be used once and is valid for 5 minutes.

View File

@ -0,0 +1,12 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Connexion au compte</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Connexion au compte</b>
</td>
</tr>
</tbody></table>
<p>Votre jeton de connexion est: [[[TOKEN]]]</p>
<p>Ce jeton ne peut être utilisé qu'une seule fois et est valide pendant 5 minutes.</p>
</div></body></html>

View File

@ -0,0 +1,4 @@
[[[SERVERNAME]]] - Connexion au compte
Votre jeton de connexion est: [[[TOKEN]]]
~
Ce jeton ne peut être utilisé qu'une seule fois et est valide pendant 5 minutes.

Some files were not shown because too many files have changed in this diff Show More