diff --git a/.vscode/settings.json b/.vscode/settings.json index b94c4dc42d..93c4d0d5bb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,7 @@ "accountcreate", "accountid", "accountremove", + "acebase", "acmd", "acmepath", "actiontype", @@ -599,6 +600,7 @@ "postflight", "poweraction", "powerevents", + "Preconfigured", "Proto", "publicid", "pushlogin", @@ -662,6 +664,7 @@ "rport", "rtpass", "rtuser", + "runas", "runasuser", "runasuseronly", "runcommand", @@ -822,6 +825,7 @@ "usercount", "userex", "userfiles", + "userfirst", "usergroupchange", "usergroups", "userid", diff --git a/agents/meshcore.js b/agents/meshcore.js index 6337f74114..d2fca767a7 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -47,6 +47,32 @@ var MESHRIGHT_NODESKTOP = 65536; var pendingSetClip = false; // This is a temporary hack to prevent multiple setclips at the same time to stop the agent from crashing. +// +// This is a helper function used by the 32 bit Windows Agent, when running on 64 bit windows. It will check if the agent is already patched for this +// and will use this helper if it is not. This helper will inject 'sysnative' into the results when calling readdirSync() on %windir%. +// +function __readdirSync_fix(path) +{ + var sysnative = false; + pathstr = require('fs')._fixwinpath(path); + if (pathstr.split('\\*').join('').toLowerCase() == process.env['windir'].toLowerCase()) { sysnative = true; } + var ret = __readdirSync_old(path); + if (sysnative) { ret.push('sysnative'); } + return (ret); +} + +if (process.platform == 'win32' && require('_GenericMarshal').PointerSize == 4 && require('os').arch() == 'x64') +{ + if (require('fs').readdirSync.version == null) + { + // + // 32 Bit Windows Agent on 64 bit Windows has not been patched for sysnative issue, so lets use our own solution + // + require('fs').__readdirSync_old = require('fs').readdirSync; + require('fs').readdirSync = __readdirSync_fix; + } +} + function bcdOK() { if (process.platform != 'win32') { return (false); } if (require('os').arch() == 'x64') { diff --git a/agents/meshinstall-linux.js b/agents/meshinstall-linux.js index a2123e20a1..403d374413 100644 --- a/agents/meshinstall-linux.js +++ b/agents/meshinstall-linux.js @@ -103,14 +103,13 @@ if (msh.agentName) { connectArgs.push('--agentName="' + msh.agentName + '"'); } function _install(parms) { + var i; var mstr = require('fs').createWriteStream(process.execPath + '.msh', { flags: 'wb' }); - mstr.write('MeshName=' + msh.MeshName + '\n'); - mstr.write('MeshType=' + msh.MeshType + '\n'); - mstr.write('MeshID=' + msh.MeshID + '\n'); - mstr.write('ServerID=' + msh.ServerID + '\n'); - mstr.write('MeshServer=' + msh.MeshServer + '\n'); - if (msh.agentName) { mstr.write('agentName=' + msh.agentName + '\n'); } - if (msh.meshServiceName) { mstr.write('meshServiceName=' + msh.meshServiceName + '\n'); } + + for (i in msh) + { + mstr.write(i + '=' + msh[i] + '\n'); + } mstr.end(); if (parms == null) { parms = []; } @@ -156,7 +155,7 @@ if (process.argv.includes('-translations')) console.log(JSON.stringify(translation)); process.exit(); } -if (process.argv.includes('-help') || (process.platform == 'linux' && process.env['XAUTHORITY']==null && process.env['DISPLAY'] == null)) +if (process.argv.includes('-help') || (process.platform == 'linux' && process.env['XAUTHORITY'] == null && process.env['DISPLAY'] == null && process.argv.length == 1)) { console.log("\n" + translation[lang].commands + ": "); if ((msh.InstallFlags & 1) == 1) diff --git a/agents/modules_meshcmd/smbios.js b/agents/modules_meshcmd/smbios.js index b149e90747..2391b8e96c 100644 --- a/agents/modules_meshcmd/smbios.js +++ b/agents/modules_meshcmd/smbios.js @@ -269,15 +269,18 @@ function SMBiosTables() this.amtInfo = function amtInfo(data) { if (!data) { throw ('no data'); } var retVal = { AMT: false }; - if (data[130] && data[130].peek().slice(0, 4).toString() == '$AMT') { + if (data[130] && data[130].peek().slice(0, 4).toString() == '$AMT') + { var amt = data[130].peek(); retVal.AMT = amt[4] ? true : false; - if (retVal.AMT) { + if (retVal.AMT) + { retVal.enabled = amt[5] ? true : false; retVal.storageRedirection = amt[6] ? true : false; retVal.serialOverLan = amt[7] ? true : false; retVal.kvm = amt[14] ? true : false; - if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro') { + if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro') + { var settings = data[131].peek(); if (settings[0] & 0x04) { retVal.TXT = (settings[0] & 0x08) ? true : false; } if (settings[0] & 0x10) { retVal.VMX = (settings[0] & 0x20) ? true : false; } @@ -295,6 +298,14 @@ function SMBiosTables() } } } + if (!retVal.AMT) + { + if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro') + { + var settings = data[131].peek(); + if ((settings[20] & 0x08) == 0x08) { retVal.AMT = true; } + } + } return (retVal); }; this.smTableTypes = { diff --git a/agents/modules_meshcore/smbios.js b/agents/modules_meshcore/smbios.js index b149e90747..2391b8e96c 100644 --- a/agents/modules_meshcore/smbios.js +++ b/agents/modules_meshcore/smbios.js @@ -269,15 +269,18 @@ function SMBiosTables() this.amtInfo = function amtInfo(data) { if (!data) { throw ('no data'); } var retVal = { AMT: false }; - if (data[130] && data[130].peek().slice(0, 4).toString() == '$AMT') { + if (data[130] && data[130].peek().slice(0, 4).toString() == '$AMT') + { var amt = data[130].peek(); retVal.AMT = amt[4] ? true : false; - if (retVal.AMT) { + if (retVal.AMT) + { retVal.enabled = amt[5] ? true : false; retVal.storageRedirection = amt[6] ? true : false; retVal.serialOverLan = amt[7] ? true : false; retVal.kvm = amt[14] ? true : false; - if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro') { + if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro') + { var settings = data[131].peek(); if (settings[0] & 0x04) { retVal.TXT = (settings[0] & 0x08) ? true : false; } if (settings[0] & 0x10) { retVal.VMX = (settings[0] & 0x20) ? true : false; } @@ -295,6 +298,14 @@ function SMBiosTables() } } } + if (!retVal.AMT) + { + if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro') + { + var settings = data[131].peek(); + if ((settings[20] & 0x08) == 0x08) { retVal.AMT = true; } + } + } return (retVal); }; this.smTableTypes = { diff --git a/agents/modules_meshcore/win-terminal.js b/agents/modules_meshcore/win-terminal.js index a0e76864c1..1c6e8e20a2 100644 --- a/agents/modules_meshcore/win-terminal.js +++ b/agents/modules_meshcore/win-terminal.js @@ -1,5 +1,5 @@ /* -Copyright 2018 Intel Corporation +Copyright 2018-2022 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -89,21 +89,50 @@ function windows_terminal() { var newCsbi = GM.CreateVariable(22); if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, newCsbi).Val == 0) { return; } - if (newCsbi.Deref(4, 2).toBuffer().readUInt16LE() != this.currentX || newCsbi.Deref(6, 2).toBuffer().readUInt16LE() != this.currentY) { - //wchar_t mywbuf[512]; - //swprintf(mywbuf, 512, TEXT("csbi.dwCursorPosition.X = %d, csbi.dwCursorPosition.Y = %d, newCsbi.dwCursorPosition.X = %d, newCsbi.dwCursorPosition.Y = %d\r\n"), csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y, newCsbi.dwCursorPosition.X, newCsbi.dwCursorPosition.Y); - //OutputDebugString(mywbuf); - - //m_viewOffset = newCsbi.srWindow.Top; - //WriteMoveCursor((SerialAgent *)this->sa, (char)(newCsbi.dwCursorPosition.Y - m_viewOffset), (char)(newCsbi.dwCursorPosition.X - m_viewOffset)); - //LowStackSendData((SerialAgent *)(this->sa), "", 0); - + if (newCsbi.Deref(4, 2).toBuffer().readUInt16LE() != this.currentX || newCsbi.Deref(6, 2).toBuffer().readUInt16LE() != this.currentY) + { + // + // Reference for CONSOLE_SCREEN_BUFFER_INFO can be found at: + // https://learn.microsoft.com/en-us/windows/console/console-screen-buffer-info-str + // + this.currentX = newCsbi.Deref(4, 2).toBuffer().readUInt16LE(); this.currentY = newCsbi.Deref(6, 2).toBuffer().readUInt16LE(); } } - this.ClearScreen = function () { + this.ClearScreen = function () + { + // + // Reference for CONSOLE_SCREEN_BUFFER_INFO can be found at: + // https://learn.microsoft.com/en-us/windows/console/console-screen-buffer-info-str + // + + // + // Reference for GetConsoleScreenBufferInfo can be found at: + // https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo + // + + // + // Reference for FillConsoleOutputCharacter can be found at: + // https://learn.microsoft.com/en-us/windows/console/fillconsoleoutputcharacter + // + + // + // Reference for FillConsoleOutputAttribute can be found at: + // https://learn.microsoft.com/en-us/windows/console/fillconsoleoutputattribute + // + + // + // Reference for SetConsoleCursorPosition can be found at: + // https://learn.microsoft.com/en-us/windows/console/setconsolecursorposition + // + + // + // Reference for SetConsoleWindowInfo can be fount at: + // https://learn.microsoft.com/en-us/windows/console/setconsolewindowinfo + // + var CONSOLE_SCREEN_BUFFER_INFO = GM.CreateVariable(22); if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO).Val == 0) { return; } @@ -132,6 +161,7 @@ function windows_terminal() { this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect); } + // This does a rudimentary check if the platform is capable of PowerShell this.PowerShellCapable = function() { if (require('os').arch() == 'x64') @@ -144,6 +174,7 @@ function windows_terminal() { } } + // Starts a Legacy Windows Terminal Session this.StartEx = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, terminalTarget) { // The older windows terminal does not support @@ -164,7 +195,9 @@ function windows_terminal() { this._stdinput = this._kernel32.GetStdHandle(STD_INPUT_HANDLE); this._stdoutput = this._kernel32.GetStdHandle(STD_OUTPUT_HANDLE); this._connected = false; - var coordScreen = GM.CreateVariable(4); + + // Coord structure can be found at: https://learn.microsoft.com/en-us/windows/console/coord-str + var coordScreen = GM.CreateVariable(4); coordScreen.Deref(0, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH); coordScreen.Deref(2, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT); @@ -172,10 +205,21 @@ function windows_terminal() { rect.Deref(4, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH - 1); rect.Deref(6, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT - 1); - if (this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect).Val == 0) { + // + // Reference for SetConsoleWindowInfo can be found at: + // https://learn.microsoft.com/en-us/windows/console/setconsolewindowinfo + // + if (this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect).Val == 0) + { throw ('Failed to set Console Screen Size'); } - if (this._kernel32.SetConsoleScreenBufferSize(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE()).Val == 0) { + + // + // Reference for SetConsoleScreenBufferSize can be found at: + // https://learn.microsoft.com/en-us/windows/console/setconsolescreenbuffersize + // + if (this._kernel32.SetConsoleScreenBufferSize(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE()).Val == 0) + { throw ('Failed to set Console Buffer Size'); } @@ -278,8 +322,16 @@ function windows_terminal() { return (this.stopping); } + // + // This function uses the SetWinEventHook() method, so we can hook + // All events between EVENT_CONSOLE_CARET and EVENT_CONSOLE_END_APPLICATION + // this._hookThread = function () { + // + // Reference for SetWinEventHook() can be found at: + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook + // var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; }); ret.userArgs = []; for (var a in arguments) @@ -292,24 +344,43 @@ function windows_terminal() { var p = this._user32.SetWinEventHook.async(EVENT_CONSOLE_CARET, EVENT_CONSOLE_END_APPLICATION, 0, this._ConsoleWinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); p.ready = ret; p.terminal = this; - p.then(function (hwinEventHook) { - if (hwinEventHook.Val == 0) { + p.then(function (hwinEventHook) + { + if (hwinEventHook.Val == 0) + { this.ready._rej('Error calling SetWinEventHook'); - } else { + } else + { this.terminal.hwinEventHook = hwinEventHook; this.ready._res(); this.terminal._GetMessage(); } }); - this._ConsoleWinEventProc.on('GlobalCallback', function (hhook, dwEvent, hwnd, idObject, idChild, idEventThread, swmsEventTime) { + // + // This is the WINEVENTPROC callback for the WinEventHook we set + // + this._ConsoleWinEventProc.on('GlobalCallback', function (hhook, dwEvent, hwnd, idObject, idChild, idEventThread, swmsEventTime) + { + // + // Reference for WINEVENTPROC can be found at: + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wineventproc + // if (!this.terminal.hwinEventHook || this.terminal.hwinEventHook.Val != hhook.Val) { return; } var buffer = null; - switch (dwEvent.Val) { + // + // Reference for Console WinEvents can be found at: + // https://learn.microsoft.com/en-us/windows/console/console-winevents + // + + switch (dwEvent.Val) + { case EVENT_CONSOLE_CARET: + // The console caret has moved break; case EVENT_CONSOLE_UPDATE_REGION: + // More than one character has changed if (!this.terminal.connected) { this.terminal.connected = true; this.terminal._stream._promise._res(); @@ -321,25 +392,30 @@ function windows_terminal() { } break; case EVENT_CONSOLE_UPDATE_SIMPLE: + // A single character has changed //console.log('UPDATE SIMPLE: [X: ' + LOWORD(idObject.Val) + ' Y: ' + HIWORD(idObject.Val) + ' Char: ' + LOWORD(idChild.Val) + ' Attr: ' + HIWORD(idChild.Val) + ']'); var simplebuffer = { data: [ Buffer.alloc(1, LOWORD(idChild.Val)) ], attributes: [ HIWORD(idChild.Val) ], width: 1, height: 1, x: LOWORD(idObject.Val), y: HIWORD(idObject.Val) }; this.terminal._SendDataBuffer(simplebuffer); break; case EVENT_CONSOLE_UPDATE_SCROLL: + // The console has scrolled //console.log('UPDATE SCROLL: [dx: ' + idObject.Val + ' dy: ' + idChild.Val + ']'); this.terminal._SendScroll(idObject.Val, idChild.Val); break; case EVENT_CONSOLE_LAYOUT: + // The console layout has changed. //console.log('CONSOLE_LAYOUT'); //snprintf( Buf, 512, "Event Console LAYOUT!\r\n"); //SendLayout(); break; case EVENT_CONSOLE_START_APPLICATION: + // A new console process has started //console.log('START APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']'); //snprintf( Buf, 512, "Event Console START APPLICATION!\r\nProcess ID: %d - Child ID: %d\r\n\r\n", (int)idObject, (int)idChild); //SendConsoleEvent(dwEvent, idObject, idChild); break; case EVENT_CONSOLE_END_APPLICATION: + // A console process has exited if (idObject.Val == this.terminal._hProcessID) { //console.log('END APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']'); @@ -360,18 +436,44 @@ function windows_terminal() { return (ret); } - this._GetMessage = function () { + // Retrieves a message from the calling thread's message queue + this._GetMessage = function () + { + // + // Reference for GetMessage() can be found at: + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage + // + + // + // Reference for TranslateMessage() can be found at: + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-translatemessage + // + + // + // Reference for DispatchMessage() can be found at: + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-dispatchmessage + // + if (this._user32.abort) { console.log('aborting loop'); return; } - this._user32.GetMessageA.async(this._user32.SetWinEventHook.async, MSG, 0, 0, 0).then(function (ret) { + this._user32.GetMessageA.async(this._user32.SetWinEventHook.async, MSG, 0, 0, 0).then(function (ret) + { //console.log('GetMessage Response'); - if (ret.Val != 0) { - if (ret.Val == -1) { + if (ret.Val != 0) + { + if (ret.Val == -1) + { // handle the error and possibly exit - } else { + } + else + { + // Translates virtual-key messages into character messages //console.log('TranslateMessage'); - this.nativeProxy._user32.TranslateMessage.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function () { + this.nativeProxy._user32.TranslateMessage.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function () + { + // Dispatches a message to a window procedure //console.log('DispatchMessage'); - this.nativeProxy._user32.DispatchMessageA.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function () { + this.nativeProxy._user32.DispatchMessageA.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function () + { this.nativeProxy.terminal._GetMessage(); }, console.log); }, console.log); @@ -384,7 +486,8 @@ function windows_terminal() { if (this.nativeProxy.terminal._hProcess == null) { return; } this.nativeProxy.terminal.stopping._res(); - if (this.nativeProxy.terminal._kernel32.TerminateProcess(this.nativeProxy.terminal._hProcess, 1067).Val == 0) { + if (this.nativeProxy.terminal._kernel32.TerminateProcess(this.nativeProxy.terminal._hProcess, 1067).Val == 0) + { var e = this.nativeProxy.terminal._kernel32.GetLastError().Val; console.log('Unable to kill Terminal Process, error: ' + e); } @@ -394,22 +497,38 @@ function windows_terminal() { console.log('REJECTED_UnhookWinEvent: ' + err); }); } - }, function (err) { + }, function (err) + { // Get Message Failed console.log('REJECTED_GETMessage: ' + err); }); } - this._WriteBuffer = function (buf) { - for (var i = 0; i < buf.length; ++i) { - if (typeof (buf) == 'string') { + + this._WriteBuffer = function (buf) + { + for (var i = 0; i < buf.length; ++i) + { + if (typeof (buf) == 'string') + { this._WriteCharacter(buf.charCodeAt(i), false); - } else { + } else + { this._WriteCharacter(buf[i], false); } } } this._WriteCharacter = function (key, bControlKey) { + // + // Reference for WriteConsoleInput() can be found at: + // https://learn.microsoft.com/en-us/windows/console/writeconsoleinput + // + + // + // Reference for INPUT_RECORD can be found at: + // https://learn.microsoft.com/en-us/windows/console/input-record-str + // + 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 @@ -427,62 +546,78 @@ function windows_terminal() { } // Get the current visible screen buffer - this._GetScreenBuffer = function (sx, sy, ex, ey) { + this._GetScreenBuffer = function (sx, sy, ex, ey) + { + // + // Reference for GetConsoleScreenBufferInfo() can be found at: + // https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo + // + + // + // Reference for ReadConsoleOutput() can be found at: + // https://learn.microsoft.com/en-us/windows/console/readconsoleoutput + // + var info = GM.CreateVariable(22); if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, info).Val == 0) { throw ('Error getting screen buffer info'); } - + var nWidth = info.Deref(14, 2).toBuffer().readUInt16LE() - info.Deref(10, 2).toBuffer().readUInt16LE() + 1; var nHeight = info.Deref(16, 2).toBuffer().readUInt16LE() - info.Deref(12, 2).toBuffer().readUInt16LE() + 1; - - if (arguments[3] == null) { + + if (arguments[3] == null) + { // Use Default Parameters sx = 0; sy = 0; ex = nWidth - 1; ey = nHeight - 1; - } else { + } else + { if (this._scrx != 0) { sx += this._scrx; ex += this._scrx; } if (this._scry != 0) { sy += this._scry; ey += this._scry; } this._scrx = this._scry = 0; } - + var nBuffer = GM.CreateVariable((ex - sx + 1) * (ey - sy + 1) * 4); var size = GM.CreateVariable(4); size.Deref(0, 2).toBuffer().writeUInt16LE(ex - sx + 1, 0); size.Deref(2, 2).toBuffer().writeUInt16LE(ey - sy + 1, 0); - + var startCoord = GM.CreateVariable(4); startCoord.Deref(0, 2).toBuffer().writeUInt16LE(0, 0); startCoord.Deref(2, 2).toBuffer().writeUInt16LE(0, 0); - + var region = GM.CreateVariable(8); region.buffer = region.toBuffer(); region.buffer.writeUInt16LE(sx, 0); region.buffer.writeUInt16LE(sy, 2); region.buffer.writeUInt16LE(ex, 4); region.buffer.writeUInt16LE(ey, 6); - - if (this._kernel32.ReadConsoleOutputA(this._stdoutput, nBuffer, size.Deref(0, 4).toBuffer().readUInt32LE(), startCoord.Deref(0, 4).toBuffer().readUInt32LE(), region).Val == 0) { + + if (this._kernel32.ReadConsoleOutputA(this._stdoutput, nBuffer, size.Deref(0, 4).toBuffer().readUInt32LE(), startCoord.Deref(0, 4).toBuffer().readUInt32LE(), region).Val == 0) + { throw ('Unable to read Console Output'); } - + // Lets convert the buffer into something simpler //var retVal = { data: Buffer.alloc((dw - dx + 1) * (dh - dy + 1)), attributes: Buffer.alloc((dw - dx + 1) * (dh - dy + 1)), width: dw - dx + 1, height: dh - dy + 1, x: dx, y: dy }; - + var retVal = { data: [], attributes: [], width: ex - sx + 1, height: ey - sy + 1, x: sx, y: sy }; var x, y, line, ifo, tmp, lineWidth = ex - sx + 1; - - for (y = 0; y <= (ey - sy) ; ++y) { + + for (y = 0; y <= (ey - sy) ; ++y) + { retVal.data.push(Buffer.alloc(lineWidth)); retVal.attributes.push(Buffer.alloc(lineWidth)); - + line = nBuffer.Deref(y * lineWidth * 4, lineWidth * 4).toBuffer(); - for (x = 0; x < lineWidth; ++x) { + for (x = 0; x < lineWidth; ++x) + { retVal.data.peek()[x] = line[x * 4]; retVal.attributes.peek()[x] = line[2 + (x * 4)]; } } - + return (retVal); } @@ -507,6 +642,11 @@ function windows_terminal() { this._SendScroll = function _SendScroll(dx, dy) { + // + // Reference for GetConsoleScreenBufferInfo() can be found at: + // https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo + // + if (this._scrollTimer || this._stream == null) { return; } var info = GM.CreateVariable(22); @@ -546,12 +686,15 @@ function LOWORD(val) { return (val & 0xFFFF); } function HIWORD(val) { return ((val >> 16) & 0xFFFF); } function GetEsc(op, args) { return (Buffer.from('\x1B[' + args.join(';') + op)); } function MeshConsole(msg) { require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": JSON.stringify(msg) }); } -function TranslateLine(x, y, data, attributes) { +function TranslateLine(x, y, data, attributes) +{ var i, fcolor, bcolor, rcolor, fbright, bbright, lastAttr, fc, bc, rc, fb, bb, esc = [], output = [GetEsc('H', [y, x])]; - if (typeof attributes == 'number') { attributes = [ attributes ]; } // If we get a single attribute, turn it into an array. + if (typeof attributes == 'number') { attributes = [attributes]; } // If we get a single attribute, turn it into an array. - for (i = 0; i < data.length; i++) { - if (lastAttr != attributes[i]) { // To boost performance, if the attribute is the same as the last one, skip this entire part. + for (i = 0; i < data.length; i++) + { + if (lastAttr != attributes[i]) + { // To boost performance, if the attribute is the same as the last one, skip this entire part. fc = (attributes[i] & 0x0007); fc = ((fc & 0x0001) << 2) + (fc & 0x0002) + ((fc & 0x0004) >> 2); // Foreground color bc = (attributes[i] & 0x0070) >> 4; @@ -559,19 +702,19 @@ function TranslateLine(x, y, data, attributes) { rc = (attributes[i] & 0x4000); // Reverse color set fb = (attributes[i] & 0x0008) >> 3; // Bright foreground set bb = (attributes[i] & 0x0080); // Bright background set - + if (rc != rcolor) { if (rc != 0) { esc.push(7); } else { esc.push(0); fcolor = 7; bcolor = 0; fbright = 0; bbright = 0; } rcolor = rc; } // Reverse Color if (fc != fcolor) { esc.push(fc + 30); fcolor = fc; } // Set the foreground color if needed if (bc != bcolor) { esc.push(bc + 40); bcolor = bc; } // Set the background color if needed if (fb != fbright) { esc.push(2 - fb); fbright = fb; } // Set the bright foreground color if needed - if (bb != bbright) { if (bb == 0) { esc.push(bcolor + 40); } else { esc.push(bcolor + 100); bbright = bb; } } // Set bright Background color if needed + if (bb != bbright) { if (bb == 0) { esc.push(bcolor + 40); } else { esc.push(bcolor + 100); bbright = bb; } } // Set bright Background color if needed if (esc.length > 0) { output.push(GetEsc('m', esc)); esc = []; } lastAttr = attributes[i]; } output.push(Buffer.from(String.fromCharCode(data[i]))); } - + return Buffer.concat(output); } diff --git a/agents/modules_meshcore/win-virtual-terminal.js b/agents/modules_meshcore/win-virtual-terminal.js index 8709af71c2..818ab6a0b5 100644 --- a/agents/modules_meshcore/win-virtual-terminal.js +++ b/agents/modules_meshcore/win-virtual-terminal.js @@ -47,20 +47,20 @@ function vt() var GM = require('_GenericMarshal'); var k32 = GM.CreateNativeProxy('kernel32.dll'); - k32.CreateMethod('CancelIoEx'); - k32.CreateMethod('CreatePipe'); - k32.CreateMethod('CreateProcessW'); - k32.CreateMethod('CreatePseudoConsole'); - k32.CreateMethod('CloseHandle'); - k32.CreateMethod('ClosePseudoConsole'); - k32.CreateMethod('GetProcessHeap'); - k32.CreateMethod('HeapAlloc'); - k32.CreateMethod('InitializeProcThreadAttributeList'); - k32.CreateMethod('ResizePseudoConsole'); - k32.CreateMethod('UpdateProcThreadAttribute'); - k32.CreateMethod('WriteFile'); - k32.CreateMethod('ReadFile'); - k32.CreateMethod('TerminateProcess'); + k32.CreateMethod('CancelIoEx'); // https://learn.microsoft.com/en-us/windows/win32/fileio/cancelioex-func + k32.CreateMethod('CreatePipe'); // https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe + k32.CreateMethod('CreateProcessW'); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw + k32.CreateMethod('CreatePseudoConsole'); // https://learn.microsoft.com/en-us/windows/console/createpseudoconsole + k32.CreateMethod('CloseHandle'); // https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle + k32.CreateMethod('ClosePseudoConsole'); // https://learn.microsoft.com/en-us/windows/console/closepseudoconsole + k32.CreateMethod('GetProcessHeap'); // https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-getprocessheap + k32.CreateMethod('HeapAlloc'); // https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc + k32.CreateMethod('InitializeProcThreadAttributeList'); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-initializeprocthreadattributelist + k32.CreateMethod('ResizePseudoConsole'); // https://learn.microsoft.com/en-us/windows/console/resizepseudoconsole + k32.CreateMethod('UpdateProcThreadAttribute'); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute + k32.CreateMethod('WriteFile'); // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile + k32.CreateMethod('ReadFile'); // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile + k32.CreateMethod('TerminateProcess'); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess var ret = { _h: GM.CreatePointer(), _consoleInput: GM.CreatePointer(), _consoleOutput: GM.CreatePointer(), _input: GM.CreatePointer(), _output: GM.CreatePointer(), k32: k32 }; var attrSize = GM.CreateVariable(8); @@ -77,18 +77,31 @@ function vt() throw ('Error calling CreatePseudoConsole()'); } + // + // Reference for STARTUPINFOEXW + // https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-startupinfoexw + // + + // + // Reference for STARTUPINFOW + // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow + // + k32.InitializeProcThreadAttributeList(0, 1, 0, attrSize); attrList = GM.CreateVariable(attrSize.toBuffer().readUInt32LE()); - var startupinfoex = GM.CreateVariable(GM.PointerSize == 8 ? 112 : 72); - startupinfoex.toBuffer().writeUInt32LE(GM.PointerSize == 8 ? 112 : 72, 0); - attrList.pointerBuffer().copy(startupinfoex.Deref(GM.PointerSize == 8 ? 104 : 68, GM.PointerSize).toBuffer()); + var startupinfoex = GM.CreateVariable(GM.PointerSize == 8 ? 112 : 72); // Create Structure, 64 bits is 112 bytes, 32 bits is 72 bytes + startupinfoex.toBuffer().writeUInt32LE(GM.PointerSize == 8 ? 112 : 72, 0); // Write buffer size + attrList.pointerBuffer().copy(startupinfoex.Deref(GM.PointerSize == 8 ? 104 : 68, GM.PointerSize).toBuffer()); // Write the reference to STARTUPINFOEX if (k32.InitializeProcThreadAttributeList(attrList, 1, 0, attrSize).Val != 0) { if (k32.UpdateProcThreadAttribute(attrList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, ret._h.Deref(), GM.PointerSize, 0, 0).Val != 0) { - if (k32.CreateProcessW(0, GM.CreateVariable(path, { wide: true }), 0, 0, 1, EXTENDED_STARTUPINFO_PRESENT, 0, 0, startupinfoex, pi).Val != 0) + if (k32.CreateProcessW(0, GM.CreateVariable(path, { wide: true }), 0, 0, 1, EXTENDED_STARTUPINFO_PRESENT, 0, 0, startupinfoex, pi).Val != 0) // Create the process to run in the pseudoconsole { + // + // Create a Stream Object, to be able to read/write data to the pseudoconsole + // ret._startupinfoex = startupinfoex; ret._process = pi.Deref(0); ret._pid = pi.Deref(GM.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE(); @@ -111,6 +124,10 @@ function vt() flush(); } }); + + // + // The ProcessInfo object is signaled when the process exits + // ds._obj = ret; ret._waiter = require('DescriptorEvents').addDescriptor(pi.Deref(0)); ret._waiter.ds = ds; @@ -151,6 +168,7 @@ function vt() ds._rpbufRead = GM.CreateVariable(4); ds.__read = function __read() { + // Asyncronously read data from the pseudoconsole this._rp = this.terminal.k32.ReadFile.async(this.terminal._output.Deref(), this._rpbuf, this._rpbuf._size, this._rpbufRead, 0); this._rp.then(function () { @@ -173,6 +191,8 @@ function vt() } throw ('Internal Error'); } + + // This evaluates whether or not the powershell binary exists this.PowerShellCapable = function () { if (require('os').arch() == 'x64') @@ -184,10 +204,14 @@ function vt() return (require('fs').existsSync(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe')); } } + + // Start the PseudoConsole with the Command Prompt this.Start = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT) { return (this.Create(process.env['windir'] + '\\System32\\cmd.exe', CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT)); } + + // Start the PseduoConsole with PowerShell this.StartPowerShell = function StartPowerShell(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT) { if (require('os').arch() == 'x64') diff --git a/apprelays.js b/apprelays.js index 59b96827d0..84de05e0eb 100644 --- a/apprelays.js +++ b/apprelays.js @@ -282,7 +282,7 @@ module.exports.CreateWebRelay = function (parent, db, args, domain) { // Construct the HTTP request var request = req.method + ' ' + req.url + ' HTTP/' + req.httpVersion + '\r\n'; - const blockedHeaders = ['origin', 'cookie', 'upgrade-insecure-requests', 'sec-ch-ua', 'sec-ch-ua-mobile', 'dnt', 'sec-fetch-user', 'sec-ch-ua-platform', 'sec-fetch-site', 'sec-fetch-mode', 'sec-fetch-dest']; // These are headers we do not forward + const blockedHeaders = ['cookie', 'upgrade-insecure-requests', 'sec-ch-ua', 'sec-ch-ua-mobile', 'dnt', 'sec-fetch-user', 'sec-ch-ua-platform', 'sec-fetch-site', 'sec-fetch-mode', 'sec-fetch-dest']; // These are headers we do not forward for (var i in req.headers) { if (blockedHeaders.indexOf(i) == -1) { request += i + ': ' + req.headers[i] + '\r\n'; } } var cookieStr = ''; for (var i in parent.webCookies) { if (cookieStr != '') { cookieStr += '; ' } cookieStr += (i + '=' + parent.webCookies[i].value); } @@ -331,7 +331,7 @@ module.exports.CreateWebRelay = function (parent, db, args, domain) { // Construct the HTTP request var request = req.method + ' ' + req.url + ' HTTP/' + req.httpVersion + '\r\n'; - const blockedHeaders = ['origin', 'cookie', 'sec-websocket-extensions']; // These are headers we do not forward + const blockedHeaders = ['cookie', 'sec-websocket-extensions']; // These are headers we do not forward for (var i in req.headers) { if (blockedHeaders.indexOf(i) == -1) { request += i + ': ' + req.headers[i] + '\r\n'; } } var cookieStr = ''; for (var i in parent.webCookies) { if (cookieStr != '') { cookieStr += '; ' } cookieStr += (i + '=' + parent.webCookies[i].value); } diff --git a/docs/docs/faq.md b/docs/docs/faq.md deleted file mode 100644 index cc4a5ff7f7..0000000000 --- a/docs/docs/faq.md +++ /dev/null @@ -1,27 +0,0 @@ -# FAQ - -## Help! I've been hacked there are weird agents appearing in my Tactical RMM - -No, you haven't. - -1. Your agent installer was scanned by an antivirus. - -2. It didn't recognize the exe. - -3. You have the option enabled to submit unknown applications for analysis. - - ![AV Option1](images/faq_av_option1.png) - -4. They ran it against their virtualization testing cluster. - -5. You allow anyone to connect to your rmm server (you should look into techniques to hide your server from the internet). - -6. Here are some examples of what that looks like. - -# Can't login on server after first setup - -You're sure you're typing in everything right, giving it 2FA code and can't login - -[TOTP](https://en.wikipedia.org/wiki/Time-based_one-time_password) is time sensitive, check your time/NTP and make sure it's right (on server and TOTP app device)! :) - -![](images/2022-08-04-18-19-19.png) diff --git a/docs/docs/install/index.md b/docs/docs/install/index.md index f278b4b9e8..59e7b8daca 100644 --- a/docs/docs/install/index.md +++ b/docs/docs/install/index.md @@ -9,7 +9,7 @@ npm install meshcentral node node_modules/meshcentral ``` -That's it. MeshCentral will set itself up and start managing computers on your local network. By default it will be setup in LAN mode and agents you install will multicast on the local network to find the server. To setup the server so that agents use a well known DNS name and to start customizing your server, go in the "meshcentral-data" folder and edit the config.json file. The configuration file must be valid JSON, you can use this link to validate the file format. +That's it. MeshCentral will set itself up and start managing computers on your local network. By default it will be setup in LAN mode and agents you install will multicast on the local network to find the server. To setup the server so that agents use a well known DNS name and to start customizing your server, go in the "meshcentral-data" folder and edit the config.json file. The configuration file must be valid JSON, you can use this [link](https://duckduckgo.com/?va=j&t=hc&q=json+lint&ia=answer) to validate the file format. For Windows users, you can download the MeshCentral Installer that will automate installation of NodeJS and provide basic configuration of the server. This option is not recommended for advanced users. diff --git a/docs/docs/meshcentral/SSLnletsencrypt.md b/docs/docs/meshcentral/SSLnletsencrypt.md new file mode 100644 index 0000000000..fe8d3f786f --- /dev/null +++ b/docs/docs/meshcentral/SSLnletsencrypt.md @@ -0,0 +1,37 @@ +# SSL/Letsencrypt + +## MeshCentral supports SSL using self generated certs, your own certs or Letsencrypt + +### Enabling letsencrypt + +Make sure you match and/or adjust all the following settings appropriately in your config.json file: + +```json +{ + "settings": { + "redirPort" + "cert": "yourdomain.com" + }, + "domains": { + "letsencrypt": { + "__comment__": "Requires NodeJS 8.x or better, Go to https://letsdebug.net/ first before trying Let's Encrypt.", + "email": "myemail@myserver.com", + "names": "myserver.com,customer1.myserver.com", + "skipChallengeVerification": false, + "production": true + }, + } +} +``` + +If you need further clarification to know what each of these settings are, check out [the config schema](https://github.com/Ylianst/MeshCentral/blob/master/meshcentral-config-schema.json). + +Then restart meshcentral and it will get a cert for you, the process will need to restart to apply the cert. + +### Useful resources/troubleshooting + +To check letsencrypt is working properly please use https://letsdebug.net/. We are using the [HTTP-O1 challenge](https://letsencrypt.org/docs/challenge-types/#http-01-challenge) method with these instructions. + +Also make sure you have port 80 open and pointing to your meshcentral server, **IT WILL NOT WORK** if port 80 isn't open and it **HAS** to be port 80. + +You can read more about Letsencrypt and meshcentral [here](https://ylianst.github.io/MeshCentral/meshcentral/#lets-encrypt-support). diff --git a/docs/docs/meshcentral/agents.md b/docs/docs/meshcentral/agents.md index 1ba89e6125..43279e6ae7 100644 --- a/docs/docs/meshcentral/agents.md +++ b/docs/docs/meshcentral/agents.md @@ -25,6 +25,8 @@ xxx Path: `c:\Program Files\Mesh Agent\meshagent.msh` ## Linux / BSD +Uninstall: `sudo /usr/local/mesh_services/meshagent/[agent-name]/meshagent -fulluninstall` + ## Apple macOS Binary Installer Default Install Path: `/usr/local/mesh_services/meshagent/meshagent` @@ -38,6 +40,10 @@ launchctl stop meshagent launchctl start meshagent ``` +Install: + +Uninstall: `sudo /usr/local/mesh_services/meshagent/[agent-name]/meshagent -fulluninstall` + ## Apple macOS Universal For OSx 11+ including Big Sur, Monterey and later diff --git a/docs/docs/meshcentral/config.md b/docs/docs/meshcentral/config.md index 8c5f35e6db..439a1dc5d1 100644 --- a/docs/docs/meshcentral/config.md +++ b/docs/docs/meshcentral/config.md @@ -356,7 +356,7 @@ See description for information about each item. "loginKey": { "type": [ "string", "array" ], "items": { "type": "string" }, "default": null, "description": "Requires that users add the value ?key=xxx in the URL in order to see the web site." }, "agentKey": { "type": [ "string", "array" ], "items": { "type": "string" }, "default": null, "description": "Requires that agents add the value ?key=xxx in the URL in order to connect. This is not automatic and needs to be manually added in the meshagent.msh file." }, "ipkvm": { "type": "boolean", "default": false, "description": "Set to true to enable IP KVM device support in this domain." }, - "minify": { "type": "boolean", "default": false, "description": "When enabled, the server will send reduced sided web pages." }, + "minify": { "type": "boolean", "default": false, "description": "When enabled, the server will send reduced sized web pages." }, "newAccounts": { "type": "boolean", "default": false, "description": "When set to true, allow new user accounts to be created from the login page." }, "newAccountsPass": { "type": "string", "default": null, "description": "When set this password will be required in order to create a new account from the login screen." }, "newAccountsCaptcha": { "type": "boolean", "default": false, "description": "When set to true, users will get a CAPTCHA when creating a new account from the login screen." }, @@ -410,12 +410,12 @@ See description for information about each item. "enum": [ "bat", "ps1", "sh", "agent" ] }, "runas": { - "description": "How to run this script, does not appy to agent scripts.", + "description": "How to run this script, does not apply to agent scripts.", "type": "string", "enum": ["agent", "userfirst", "user"] }, "cmd": { - "description": "The command or \\r\\n seperated commands to run, if set do not use the file key.", + "description": "The command or \\r\\n separated commands to run, if set do not use the file key.", "type": "string" }, "file": { @@ -562,13 +562,13 @@ See description for information about each item. "ldapSyncWithUserGroups": { "type": [ "boolean", "object" ], "default": false, - "description": "When set to true or set to an object, MeshCentral will syncronized LDAP user memberships to MeshCentral user groups.", + "description": "When set to true or set to an object, MeshCentral will synchronize LDAP user memberships to MeshCentral user groups.", "additionalProperties": false, "properties": { "filter": { "type": [ "string", "array" ], "default": null, - "description": "When set to a string or array of strings, only LDAP membership groups that includes one of the strings will be syncronized with MeshCentral user groups." + "description": "When set to a string or array of strings, only LDAP membership groups that includes one of the strings will be synchronized with MeshCentral user groups." } } }, diff --git a/docs/docs/meshcentral/faq.md b/docs/docs/meshcentral/faq.md new file mode 100644 index 0000000000..421b99771c --- /dev/null +++ b/docs/docs/meshcentral/faq.md @@ -0,0 +1,50 @@ +# FAQ + +## json config files + +Any item in the config.json file starting with an underscore character are ignored. + +Ignored + +```json +"_title": "MyServer" +``` + +Valid setting + +```json +"title": "MyServer" +``` + +json requires correct formatting, if in doubt copy/paste your json config into a web based format checker to make sure you have it right: + +## Help! I've been hacked there are weird agents appearing in my MeshCentral Console + +No, you haven't. + +1. Your agent installer was scanned by an antivirus. + +2. It didn't recognize the exe. + +3. You have the option enabled to submit unknown applications for analysis. + + ![AV Option1](images/faq_av_option1.png) + +4. They ran it against their virtualization testing cluster. + +5. You allow anyone to connect to your server (you should look into techniques to hide your server from the internet). + +6. Here are some examples of what that looks like. + +# Can't login on server after first setup + +You're sure you're typing in everything right, giving it 2FA code and can't login + +[TOTP](https://en.wikipedia.org/wiki/Time-based_one-time_password) is time sensitive, check your time/NTP and make sure it's right (on server and TOTP app device)! :) + +![](images/2022-08-04-18-19-19.png) + +# Branding and Customisation + +You can brand and customise MeshCentral almost as much as you like without delving into the code, a few changes in the config.json file and uplaoding images can change the way your system looks. Read more [here](https://ylianst.github.io/MeshCentral/meshcentral/#branding-terms-of-use) + diff --git a/docs/docs/images/faq_av_option1.png b/docs/docs/meshcentral/images/faq_av_option1.png similarity index 100% rename from docs/docs/images/faq_av_option1.png rename to docs/docs/meshcentral/images/faq_av_option1.png diff --git a/docs/docs/meshcentral/index.md b/docs/docs/meshcentral/index.md index ba4524306d..3ebd240f5a 100644 --- a/docs/docs/meshcentral/index.md +++ b/docs/docs/meshcentral/index.md @@ -1201,7 +1201,11 @@ And taking authentication to the next step is removing the login page entirely. ## Branding & Terms of use -Once MeshCentral is setup, you may want to customize the web site with your own brand and terms of use. This is important to personalize the web site to your organization. We also want to customize the web site in such a way that updating to the latest version will keep the branding as-is. +Whitelabeling your MeshCentral installation to personalize it to your companies brand, as well as having your own terms of use is one of the first things many people do after installation. + +
+ +
### Branding @@ -1209,7 +1213,7 @@ You can put you own logo on the top of the web page. To get started, get the fil ![](images/2022-05-19-00-38-51.png) -Once done, edit the config.json file and set the following values: +Once done, edit the config.json file and set one or all of the following values: ```json "domains": { @@ -1217,6 +1221,16 @@ Once done, edit the config.json file and set the following values: "Title": "", "Title2": "", "TitlePicture": "title-sample.png", + "loginPicture": "logintitle-sample.png", + "welcomeText": "This is sample text", + "welcomePicture": "mainwelcome-04.jpg", + "welcomePictureFullScreen": true, + "siteStyle": "1", + "nightMode": "1", + "meshMessengerTitle": "Mesh Chat", + "meshMessengerPicture": "chatimage.png", + "footer": "This is a HTML string displayed at the bottom of the web page when a user is logged in.", + "loginfooter": "This is a HTML string displayed at the bottom of the web page when a user is not logged in." }, ``` @@ -1240,6 +1254,8 @@ This is great to personalize the look of the server within the web site. ### Agent Branding +You can also customize the Agent to add your own logo. + ![](images/2022-08-24-06-42-40.png) ### Terms of use diff --git a/docs/docs/tipsntricks.md b/docs/docs/meshcentral/tipsntricks.md similarity index 100% rename from docs/docs/tipsntricks.md rename to docs/docs/meshcentral/tipsntricks.md diff --git a/docs/docs/stylesheets/extra.css b/docs/docs/stylesheets/extra.css index ef3b605001..721e0b3357 100644 --- a/docs/docs/stylesheets/extra.css +++ b/docs/docs/stylesheets/extra.css @@ -1,3 +1,8 @@ +/* Maximum space for text block */ +.md-grid { + max-width: 95%; /* or 100%, if you want to stretch to full-width */ + } + .md-header { background-color: #0b3e81 !important; color: white !important; diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index daa840e812..23285be2da 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -15,9 +15,12 @@ nav: - 'Debugging': 'meshcentral/debugging.md' - 'Device Tabs': 'meshcentral/devicetabs.md' - 'Plugins': 'meshcentral/plugins.md' + - 'SSL': 'meshcentral/SSLnletsencrypt.md' - 'Security': 'meshcentral/security.md' - 'Tokens': 'meshcentral/tokens.md' - + - 'FAQ': 'meshcentral/faq.md' + - 'Tips n Tricks': 'meshcentral/tipsntricks.md' + - Design and Architecture: - design/index.md diff --git a/meshcentral-config-schema.json b/meshcentral-config-schema.json index 916034ad4b..c35ac0ac96 100644 --- a/meshcentral-config-schema.json +++ b/meshcentral-config-schema.json @@ -349,7 +349,7 @@ "loginKey": { "type": [ "string", "array" ], "items": { "type": "string" }, "default": null, "description": "Requires that users add the value ?key=xxx in the URL in order to see the web site." }, "agentKey": { "type": [ "string", "array" ], "items": { "type": "string" }, "default": null, "description": "Requires that agents add the value ?key=xxx in the URL in order to connect. This is not automatic and needs to be manually added in the meshagent.msh file." }, "ipkvm": { "type": "boolean", "default": false, "description": "Set to true to enable IP KVM device support in this domain." }, - "minify": { "type": "boolean", "default": false, "description": "When enabled, the server will send reduced sided web pages." }, + "minify": { "type": "boolean", "default": false, "description": "When enabled, the server will send reduced sized web pages." }, "newAccounts": { "type": "boolean", "default": false, "description": "When set to true, allow new user accounts to be created from the login page." }, "newAccountsPass": { "type": "string", "default": null, "description": "When set this password will be required in order to create a new account from the login screen." }, "newAccountsCaptcha": { "type": "boolean", "default": false, "description": "When set to true, users will get a CAPTCHA when creating a new account from the login screen." }, @@ -403,12 +403,12 @@ "enum": [ "bat", "ps1", "sh", "agent" ] }, "runas": { - "description": "How to run this script, does not appy to agent scripts.", + "description": "How to run this script, does not apply to agent scripts.", "type": "string", "enum": ["agent", "userfirst", "user"] }, "cmd": { - "description": "The command or \\r\\n seperated commands to run, if set do not use the file key.", + "description": "The command or \\r\\n separated commands to run, if set do not use the file key.", "type": "string" }, "file": { @@ -555,13 +555,13 @@ "ldapSyncWithUserGroups": { "type": [ "boolean", "object" ], "default": false, - "description": "When set to true or set to an object, MeshCentral will syncronized LDAP user memberships to MeshCentral user groups.", + "description": "When set to true or set to an object, MeshCentral will synchronize LDAP user memberships to MeshCentral user groups.", "additionalProperties": false, "properties": { "filter": { "type": [ "string", "array" ], "default": null, - "description": "When set to a string or array of strings, only LDAP membership groups that includes one of the strings will be syncronized with MeshCentral user groups." + "description": "When set to a string or array of strings, only LDAP membership groups that includes one of the strings will be synchronized with MeshCentral user groups." } } }, diff --git a/meshcentral.js b/meshcentral.js index fa7f304b30..11f181e462 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -1420,7 +1420,7 @@ function CreateMeshCentralServer(config, args) { if (obj.args.mpsaliasport != null && (typeof obj.args.mpsaliasport != 'number')) obj.args.mpsaliasport = null; if (obj.args.rediraliasport != null && (typeof obj.args.rediraliasport != 'number')) obj.args.rediraliasport = null; if (obj.args.redirport == null) obj.args.redirport = 80; - if (obj.args.minifycore === 0) obj.args.minifycore = false; + if (obj.args.minifycore == null) obj.args.minifycore = false; if (typeof args.agentidletimeout != 'number') { args.agentidletimeout = 150000; } else { args.agentidletimeout *= 1000 } // Default agent idle timeout is 2m, 30sec. if ((obj.args.lanonly != true) && (obj.args.webrtconfig == null)) { obj.args.webrtconfig = { iceservers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun.services.mozilla.com' }] }; } // Setup default WebRTC STUN servers if (typeof obj.args.ignoreagenthashcheck == 'string') { if (obj.args.ignoreagenthashcheck == '') { delete obj.args.ignoreagenthashcheck; } else { obj.args.ignoreagenthashcheck = obj.args.ignoreagenthashcheck.split(','); } } diff --git a/meshctrl.js b/meshctrl.js index 57b3c838ab..33a5b6f13e 100644 --- a/meshctrl.js +++ b/meshctrl.js @@ -579,6 +579,7 @@ if (args['_'].length == 0) { console.log("\r\nOptional arguments:\r\n"); console.log(" --desc [description] - New group description."); console.log(" --amtonly - New group is agent-less, Intel AMT only."); + console.log(" --agentless - New group is agent-less only."); console.log(" --features [number] - Set device group features, sum of numbers below."); console.log(" 1 = Auto-Remove 2 = Hostname Sync"); console.log(" 4 = Record Sessions"); @@ -1356,6 +1357,7 @@ function serverConnect() { var op = { action: 'createmesh', meshname: args.name, meshtype: 2, responseid: 'meshctrl' }; if (args.desc) { op.desc = args.desc; } if (args.amtonly) { op.meshtype = 1; } + if (args.agentless) { op.meshtype = 3; } if (args.features) { op.flags = parseInt(args.features); } if (args.consent) { op.consent = parseInt(args.consent); } ws.send(JSON.stringify(op)); diff --git a/translate/translate.json b/translate/translate.json index 5f4038e117..1ccec14ace 100644 --- a/translate/translate.json +++ b/translate/translate.json @@ -494,6 +494,8 @@ { "bs": "“ zatražio pomoć.", "en": "\" requested help.", + "nl": "\" vraagt om hulp.", + "pl": "\" prosi o pomoc.", "xloc": [ "device-help.html->2->3" ] @@ -5939,6 +5941,7 @@ "bs": "Zapisi tokena za prijavu na račun", "en": "Account login token records", "nl": "Token vermeldingen voor accountaanmelding", + "pl": "Zapisy logowania tokenami użytkownika", "xloc": [ "default.handlebars->47->2918" ] @@ -9639,6 +9642,7 @@ "bs": "Došlo je do nepoznate greške.", "en": "An unknown error occured.", "nl": "Er is een onbekende fout opgetreden.", + "pl": "Wystąpił nieznany błąd.", "xloc": [ "default.handlebars->47->2798" ] @@ -15761,6 +15765,7 @@ "bs": "Naredbe iz datoteke", "en": "Commands from file", "nl": "Opdrachten uit bestand", + "pl": "Komendy z pliku", "xloc": [ "default.handlebars->47->730" ] @@ -15769,6 +15774,7 @@ "bs": "Komande iz datoteke na serveru", "en": "Commands from file on server", "nl": "Opdrachten uit een bestand op de server", + "pl": "Komendy z pliku na serwerze", "xloc": [ "default.handlebars->47->731" ] @@ -15777,6 +15783,7 @@ "bs": "Naredbe iz okvira za tekst", "en": "Commands from text box", "nl": "Opdrachten uit tekstvak", + "pl": "Komendy z okienka tekstowego", "xloc": [ "default.handlebars->47->729" ] @@ -15939,6 +15946,7 @@ "bs": "Zapisi konfiguracionih datoteka", "en": "Configuration file records", "nl": "Configuratiebestand vermeldingen", + "pl": "Zapisy pliku konfiguracyjnego", "xloc": [ "default.handlebars->47->2911" ] @@ -19523,6 +19531,7 @@ "bs": "Zapisi baze podataka", "en": "Database Records", "nl": "Database vermeldingen", + "pl": "Zapisy Bazy Danych", "xloc": [ "default.handlebars->47->2837" ] @@ -20402,6 +20411,8 @@ { "bs": "Odbijena prijava korisnika sa {0}, {1}, {2}", "en": "Denied user login from {0}, {1}, {2}", + "nl": "Geweigerde gebruikerslogin van {0}, {1}, {2}", + "pl": "Odmówiono zalogowania użytkownika z {0}, {1}, {2}", "xloc": [ "default.handlebars->47->2406" ] @@ -21210,6 +21221,8 @@ { "bs": "uređaj \"", "en": "Device \"", + "nl": "Apparaat \"", + "pl": "Urządzenie \"", "xloc": [ "device-help.html->2->3" ] @@ -21217,6 +21230,8 @@ { "bs": "Uređaj \"[[[DEVICENAME]]]\" je zatražio pomoć.", "en": "Device \"[[[DEVICENAME]]]\" requested assistance.", + "nl": "Apparaat \"[[[DEVICENAME]]]\" heeft assistentie gevraagd", + "pl": "Urządzenie \"[[[DEVICENAME]]]\" prosiło o pomoc.", "xloc": [ "device-help.txt" ] @@ -21643,6 +21658,7 @@ "bs": "Zapis push obavijesti uređaja", "en": "Device Push Notification Record", "nl": "Apparaat Push Notificatie vermelding", + "pl": "Zapis Powiadomień Push Urządzenia", "xloc": [ "default.handlebars->47->2920" ] @@ -21651,6 +21667,7 @@ "bs": "SMBIOS zapis uređaja", "en": "Device SMBIOS record", "nl": "Apparaat SMBIOS vermelding", + "pl": "Zapis SMBIOS urządzenia", "xloc": [ "default.handlebars->47->2919" ] @@ -22029,6 +22046,7 @@ "bs": "Zapis grupe uređaja", "en": "Device group record", "nl": "Apparaatgroep vermelding", + "pl": "Zapis grupy urządzeń", "xloc": [ "default.handlebars->47->2905" ] @@ -22115,6 +22133,7 @@ "bs": "Zapisi informacija o uređaju", "en": "Device information records", "nl": "Apparaatinformatie vermeldingen", + "pl": "Zapisy informacji urządzenia", "xloc": [ "default.handlebars->47->2907" ] @@ -22657,6 +22676,7 @@ "bs": "Zapisi o prelasku napajanja uređaja", "en": "Device power transition records", "nl": "Apparaat aan/uit vermeldingen", + "pl": "Zapisy zmiany stanu zasilania", "xloc": [ "default.handlebars->47->2913" ] @@ -22665,6 +22685,7 @@ "bs": "Zapis uređaja", "en": "Device record", "nl": "Apparaat vermelding", + "pl": "Zapis urządzenia", "xloc": [ "default.handlebars->47->2904" ] @@ -22725,6 +22746,7 @@ "bs": "Zapisi o dijeljenju uređaja", "en": "Device sharing records", "nl": "Apparaten delings vermeldingen", + "pl": "Zapisy udostępniania urządzenia", "xloc": [ "default.handlebars->47->2917" ] @@ -22733,6 +22755,7 @@ "bs": "Uređaj, korisnici, grupe i drugi zapisi", "en": "Device, users, groups and other records", "nl": "Apparaat, gebruikers, groepen en andere vermeldingen", + "pl": "Zapisy urządzenia, użytkowników, grup i inne", "xloc": [ "default.handlebars->47->2921" ] @@ -25980,6 +26003,8 @@ { "bs": "Pošaljite zahtjev za pomoć e-poštom", "en": "Email Help Request", + "nl": "E-mail hulpverzoek", + "pl": "Prośba Pomocy Email", "xloc": [ "default.handlebars->47->1939", "default.handlebars->47->889" @@ -28036,6 +28061,7 @@ "bs": "Evidencija događaja", "en": "Event records", "nl": "Gebeurtenis vermeldingen", + "pl": "Zapisy zdarzeń", "xloc": [ "default.handlebars->47->2914" ] @@ -31158,6 +31184,8 @@ { "bs": "Idi na prvu stranicu", "en": "Go to first page", + "nl": "Ga naar de eerste pagina", + "pl": "Idź do pierwszej strony", "xloc": [ "default.handlebars->container->column_l->p1->p1title->devListToolbarViewIcons" ] @@ -31165,6 +31193,8 @@ { "bs": "Idi na posljednju stranicu", "en": "Go to last page", + "nl": "Ga naar de laatste pagina ", + "pl": "Idź do ostatniej strony", "xloc": [ "default.handlebars->container->column_l->p1->p1title->devListToolbarViewIcons" ] @@ -31226,6 +31256,8 @@ { "bs": "Idi na sljedeću stranicu", "en": "Go to next page", + "nl": "Ga naar de volgende pagina", + "pl": "Idź do następnej strony", "xloc": [ "default.handlebars->container->column_l->p1->p1title->devListToolbarViewIcons" ] @@ -31233,6 +31265,8 @@ { "bs": "Idi na prethodnu stranicu", "en": "Go to previous page", + "nl": "Ga naar de vorige pagina", + "pl": "Idź do poprzedniej strony", "xloc": [ "default.handlebars->container->column_l->p1->p1title->devListToolbarViewIcons" ] @@ -31620,6 +31654,8 @@ { "bs": "Tip grupe", "en": "Group Type", + "nl": "Groepstype", + "pl": "Typ Grupy", "xloc": [ "default.handlebars->47->2576" ] @@ -32022,6 +32058,23 @@ { "bs": "HTTP/", "en": "HTTP/", + "nl": "HTTP/", + "cs": "HTTP/", + "es": "HTTP/", + "de": "HTTP/", + "da": "HTTP/", + "fi": "HTTP/", + "fr": "HTTP/", + "hi": "HTTP/", + "it": "HTTP/", + "ja": "HTTP/", + "ko": "HTTP/", + "pt": "HTTP/", + "pt-br": "HTTP/", + "pl": "HTTP/", + "sv": "HTTP/", + "ru": "HTTP/", + "tr": "HTTP/", "xloc": [ "default.handlebars->47->1004", "default.handlebars->47->1981" @@ -32030,6 +32083,8 @@ { "bs": "HTTP/{0} link", "en": "HTTP/{0} link", + "nl": "HTTP/{0} link", + "pl": "HTTP/{0} link", "xloc": [ "default.handlebars->47->298" ] @@ -32116,6 +32171,23 @@ { "bs": "HTTPS/", "en": "HTTPS/", + "nl": "HTTPS/", + "cs": "HTTPS/", + "es": "HTTPS/", + "de": "HTTPS/", + "da": "HTTPS/", + "fi": "HTTPS/", + "fr": "HTTPS/", + "hi": "HTTPS/", + "it": "HTTPS/", + "ja": "HTTPS/", + "ko": "HTTPS/", + "pt": "HTTPS/", + "pt-br": "HTTPS/", + "sv": "HTTPS/", + "ru": "HTTPS/", + "tr": "HTTPS/", + "pl": "HTTPS/", "xloc": [ "default.handlebars->47->1005", "default.handlebars->47->1982" @@ -32124,6 +32196,23 @@ { "bs": "HTTPS/{0}", "en": "HTTPS/{0}", + "nl": "HTTPS/{0}", + "pl": "HTTPS/{0}", + "cs": "HTTPS/{0}", + "es": "HTTPS/{0}", + "de": "HTTPS/{0}", + "da": "HTTPS/{0}", + "fi": "HTTPS/{0}", + "fr": "HTTPS/{0}", + "hi": "HTTPS/{0}", + "it": "HTTPS/{0}", + "ja": "HTTPS/{0}", + "ko": "HTTPS/{0}", + "pt": "HTTPS/{0}", + "pt-br": "HTTPS/{0}", + "sv": "HTTPS/{0}", + "ru": "HTTPS/{0}", + "tr": "HTTPS/{0}", "xloc": [ "default.handlebars->47->299" ] @@ -32524,6 +32613,8 @@ { "bs": "Zahtjevi za pomoć", "en": "Help requests", + "nl": "Hulpverzoeken", + "pl": "Prośby pomocy", "xloc": [ "default.handlebars->47->1027", "default.handlebars->47->2179" @@ -33126,6 +33217,7 @@ "bs": "Zapisi informacija o IP lokaciji", "en": "IP location information records", "nl": "IP locatiegegevens vermeldingen", + "pl": "Zapisy o informacji lokalizacji IP", "xloc": [ "default.handlebars->47->2908" ] @@ -35244,6 +35336,8 @@ { "bs": "Intel® AMT Uključite BIOS", "en": "Intel® AMT Power on to BIOS", + "nl": "Intel® AMT opstarten naar de BIOS", + "pl": "Intel® AMT Uruchom do BIOS", "xloc": [ "default.handlebars->47->1145" ] @@ -35306,6 +35400,8 @@ { "bs": "Intel® AMT Resetujte BIOS", "en": "Intel® AMT Reset to BIOS", + "nl": "Intel® AMT reset naar de BIOS", + "pl": "Intel® AMT Resetuj do BIOS", "xloc": [ "default.handlebars->47->1144" ] @@ -38838,6 +38934,7 @@ "bs": "Zabilježeno vrijeme posljednje veze", "en": "Last connection time records", "nl": "Laatste verbindingstijd vermeldingen", + "pl": "Zapisy czasu ostatnich połączeń", "xloc": [ "default.handlebars->47->2912" ] @@ -45805,6 +45902,7 @@ "bs": "Zapisi informacija o mrežnom interfejsu", "en": "Network interface information records", "nl": "Netwerkinterface informatie vermeldingen", + "pl": "Zapisy informacji o interfejsach sieciowych", "xloc": [ "default.handlebars->47->2910" ] @@ -51426,6 +51524,8 @@ { "bs": "Uključite Intel® AMT napajanje u BIOS?", "en": "Perform Intel® AMT power on to BIOS?", + "nl": "Perform Intel® AMT opstarten naar de BIOS?", + "pl": "Czy wykonać Intel® AMT uruchomienie do BIOS?", "xloc": [ "default.handlebars->47->1168" ] @@ -51460,6 +51560,8 @@ { "bs": "Izvršiti Intel® AMT resetovanje na BIOS?", "en": "Perform Intel® AMT reset to BIOS?", + "nl": "Perform Intel® AMT resetten naar de BIOS?", + "pl": "Czy wykonać Intel® AMT resetowanie do BIOS?", "xloc": [ "default.handlebars->47->1170" ] @@ -52321,6 +52423,8 @@ { "bs": "Port", "en": "Port", + "nl": "Poort", + "pl": "Port", "xloc": [ "default.handlebars->47->1126", "default.handlebars->47->1127" @@ -57733,6 +57837,8 @@ { "bs": "Zahtjev:", "en": "Request:", + "nl": "Verzoek:", + "pl": "Prośba:", "xloc": [ "device-help.html->2->5" ] @@ -57740,6 +57846,8 @@ { "bs": "Zahtjev: \"[[[UPIT ZA POMOĆ]]]\"", "en": "Request: \"[[[HELPREQUEST]]]\"", + "nl": "Verzoek: \"[[[HELPREQUEST]]]\"", + "pl": "Prośba: \"[[[HELPREQUEST]]]\"", "xloc": [ "device-help.txt" ] @@ -58650,6 +58758,7 @@ "bs": "Trči", "en": "Run", "nl": "Uitvoeren", + "pl": "Wykonaj", "xloc": [ "default.handlebars->47->924" ] @@ -58712,6 +58821,7 @@ "bs": "Pokrenite skriptu na ovom računaru", "en": "Run a script on this computer", "nl": "Voer een script uit op deze computer", + "pl": "Wykonaj skrypt na tym komputerze", "xloc": [ "default.handlebars->container->column_l->p11->deskarea0->deskarea4->1" ] @@ -62381,6 +62491,7 @@ "bs": "Server ne može preuzeti snimke iz baze podataka.", "en": "Server is unable to get recordings from the database.", "nl": "Server kan geen opnamen uit de database halen.", + "pl": "Serwer nie może pobrać zapisów z bazy danych.", "xloc": [ "default.handlebars->47->2797" ] @@ -62389,6 +62500,7 @@ "bs": "Server ne može čitati iz foldera sa snimcima.", "en": "Server is unable to read from the recordings folder.", "nl": "Server kan niet lezen uit de opnamemap.", + "pl": "Serwer nie może odczytać zapisów z folderu.", "xloc": [ "default.handlebars->47->2796" ] @@ -62397,6 +62509,7 @@ "bs": "Statistički zapisi servera", "en": "Server statistics records", "nl": "Server statistieken vermeldingen", + "pl": "Zapisy statystyk serwera", "xloc": [ "default.handlebars->47->2915" ] @@ -68186,6 +68299,7 @@ "bs": "Zapisi tekstualnih bilješki", "en": "Text notes records", "nl": "Tekst notities vermeldingen", + "pl": "Zapisy notatek tekstowych", "xloc": [ "default.handlebars->47->2909" ] @@ -68580,6 +68694,8 @@ { "bs": "Ova verzija NodeJS-a ne podržava OpenID.", "en": "This NodeJS version does not support OpenID.", + "nl": "Deze NodeJS-versie ondersteunt OpenID niet.", + "pl": "Obecna wersja NodeJS nie wspiera OpenID.", "xloc": [ "default.handlebars->47->107" ] @@ -71483,6 +71599,7 @@ "bs": "Nije moguće učitati fajl ikone agenta: {0}.", "en": "Unable to load agent icon file: {0}.", "nl": "Kan agent pictogram bestand niet laden: {0}.", + "pl": "Nie moge załadować pliku ikony agenta: {0}.", "xloc": [ "default.handlebars->47->105" ] @@ -71491,6 +71608,7 @@ "bs": "Nije moguće učitati fajl logotipa agenta: {0}.", "en": "Unable to load agent logo file: {0}.", "nl": "Kan agent logo bestand niet laden: {0}.", + "pl": "Nie moge załadować pliku logo agenta: {0}.", "xloc": [ "default.handlebars->47->106" ] @@ -73211,6 +73329,8 @@ { "bs": "Dnevnik provjere autentičnosti korisnika", "en": "User Authentication Log", + "nl": "Gebruikersauthenticatielogboek", + "pl": "Dziennik Autentykacji Użytkownika", "xloc": [ "default.handlebars->47->3053" ] @@ -73939,6 +74059,7 @@ "bs": "Zapisi grupe korisnika", "en": "User group records", "nl": "Gebruikersgroep vermeldingen", + "pl": "Zapisy grupy użytkownika", "xloc": [ "default.handlebars->47->2916" ] @@ -74056,6 +74177,7 @@ "bs": "Korisnički zapisi", "en": "User records", "nl": "Gebruikers vermeldingen", + "pl": "Zapisy użytkownika", "xloc": [ "default.handlebars->47->2906" ] @@ -74090,6 +74212,8 @@ { "bs": "Korisnik:", "en": "User:", + "nl": "Gebruiker:", + "pl": "Użytkownik:", "xloc": [ "device-help.html->2->5" ] @@ -74097,6 +74221,8 @@ { "bs": "Korisnik: \"[[[IME POMOĆI]]]\"", "en": "User: \"[[[HELPUSERNAME]]]\"", + "nl": "Gebruiker: \"[[[HELPUSERNAME]]]\"", + "pl": "Użytkownik: \"[[[HELPUSERNAME]]]\"", "xloc": [ "device-help.txt" ] @@ -77959,6 +78085,8 @@ { "bs": "[[[NAZIV UREĐAJA]]]", "en": "[[[DEVICENAME]]]", + "nl": "[[[DEVICENAME]]]", + "pl": "[[[DEVICENAME]]]", "xloc": [ "device-help.html->2->3->1" ] @@ -77992,6 +78120,22 @@ { "bs": "[[[UPIT ZA POMOĆ]]]", "en": "[[[HELPREQUEST]]]", + "nl": "[[[HELPREQUEST]]]", + "pl": "[[[HELPREQUEST]]]", + "cs": "[[[HELPREQUEST]]]", + "de": "[[[HELPREQUEST]]]", + "es": "[[[HELPREQUEST]]]", + "fi": "[[[HELPREQUEST]]]", + "it": "[[[HELPREQUEST]]]", + "hi": "[[[HELPREQUEST]]]", + "fr": "[[[HELPREQUEST]]]", + "ja": "[[[HELPREQUEST]]]", + "ko": "[[[HELPREQUEST]]]", + "pt": "[[[HELPREQUEST]]]", + "pt-br": "[[[HELPREQUEST]]]", + "ru": "[[[HELPREQUEST]]]", + "sv": "[[[HELPREQUEST]]]", + "tr": "[[[HELPREQUEST]]]", "xloc": [ "device-help.html->2->5->4" ] @@ -77999,6 +78143,22 @@ { "bs": "[[[IME POMOĆI]]]", "en": "[[[HELPUSERNAME]]]", + "nl": "[[[HELPUSERNAME]]]", + "pl": "[[[HELPUSERNAME]]]", + "cs": "[[[HELPUSERNAME]]]", + "de": "[[[HELPUSERNAME]]]", + "es": "[[[HELPUSERNAME]]]", + "fi": "[[[HELPUSERNAME]]]", + "it": "[[[HELPUSERNAME]]]", + "hi": "[[[HELPUSERNAME]]]", + "fr": "[[[HELPUSERNAME]]]", + "ja": "[[[HELPUSERNAME]]]", + "ko": "[[[HELPUSERNAME]]]", + "pt": "[[[HELPUSERNAME]]]", + "pt-br": "[[[HELPUSERNAME]]]", + "ru": "[[[HELPUSERNAME]]]", + "sv": "[[[HELPUSERNAME]]]", + "tr": "[[[HELPUSERNAME]]]", "xloc": [ "device-help.html->2->5->1" ] @@ -78034,6 +78194,8 @@ { "bs": "[[[SERVERNAME]]] - \"[[[DEVICENAME]]]\" Zahtjev za pomoć", "en": "[[[SERVERNAME]]] - \"[[[DEVICENAME]]]\" Help Request", + "nl": "[[[SERVERNAME]]] - \"[[[DEVICENAME]]]\" Verzoek om hulp", + "pl": "[[[SERVERNAME]]] - \"[[[DEVICENAME]]]\" Prośba Pomocy", "xloc": [ "device-help.html->0" ] @@ -78150,6 +78312,8 @@ { "bs": "[[[SERVERNAME]]] - Zahtjev za pomoć uređaja", "en": "[[[SERVERNAME]]] - Device Help Request", + "nl": "[[[SERVERNAME]]] - Een verzoek om het apparaat te helpen", + "pl": "[[[SERVERNAME]]] - Prośba Pomocy Urządzenia", "xloc": [ "device-help.txt" ] @@ -78212,6 +78376,8 @@ { "bs": "[[[SERVERNAME]]] - Zahtjev za pomoć", "en": "[[[SERVERNAME]]] - Help Request", + "nl": "[[[SERVERNAME]]] - Verzoek om hulp", + "pl": "[[[SERVERNAME]]] - Prośba Pomocy", "xloc": [ "device-help.html->2->1->1->0->1->1" ] @@ -78273,6 +78439,8 @@ { "bs": "[[[SERVERURL]]]?viewmode=10&gotonode=[[[NODEID]]]", "en": "[[[SERVERURL]]]?viewmode=10&gotonode=[[[NODEID]]]", + "nl": "[[[SERVERURL]]]?viewmode=10&gotonode=[[[NODEID]]]", + "pl": "[[[SERVERURL]]]?viewmode=10&gotonode=[[[NODEID]]]", "xloc": [ "device-help.txt" ] @@ -80581,6 +80749,8 @@ { "bs": "za navigaciju do ovog uređaja.", "en": "to navigate to this device.", + "nl": "naar dit apparaat navigeren.", + "pl": "by nawigować do tego urządzenia.", "xloc": [ "device-help.html->2->7" ] @@ -82855,4 +83025,4 @@ ] } ] -} \ No newline at end of file +}