Beyhan Oğur 5 цаг өмнө
commit
7bab1d906a
100 өөрчлөгдсөн 16358 нэмэгдсэн , 0 устгасан
  1. 14 0
      .dockerignore
  2. 1 0
      .gitattributes
  3. 315 0
      .gitignore
  4. 1 0
      .npmrc
  5. 25 0
      .vscode/launch.json
  6. 922 0
      .vscode/settings.json
  7. 60 0
      .vscode/tasks.json
  8. 65 0
      CODE_OF_CONDUCT.md
  9. 2 0
      CreateSourcePackage.bat
  10. 201 0
      LICENSE
  11. 22 0
      MeshCentral.sln
  12. 817 0
      MeshCentralServer.njsproj
  13. 105 0
      SECURITY.md
  14. 13 0
      SourceFileList.txt
  15. BIN
      agents/MeshAgentOSXPackager.zip
  16. BIN
      agents/MeshCentralAssistant.exe
  17. BIN
      agents/MeshCentralRouter.dmg
  18. BIN
      agents/MeshCentralRouter.exe
  19. BIN
      agents/MeshCentralRouterCmd.exe
  20. BIN
      agents/MeshCentralSatellite.exe
  21. BIN
      agents/MeshCmd.exe
  22. BIN
      agents/MeshCmd64.exe
  23. BIN
      agents/MeshCmdARM64.exe
  24. BIN
      agents/MeshCommander-Small.gz
  25. BIN
      agents/MeshService.exe
  26. BIN
      agents/MeshService64.exe
  27. BIN
      agents/MeshServiceARM64.exe
  28. 771 0
      agents/agent-translations.json
  29. 471 0
      agents/agentrecoverycore.js
  30. BIN
      agents/codesign.cer
  31. 9 0
      agents/compressModules.bat
  32. 8 0
      agents/compressRemove.bat
  33. 1 0
      agents/hashagents.bat
  34. 44 0
      agents/hashagents.js
  35. 140 0
      agents/hashagents.json
  36. BIN
      agents/meshagent_aarch64
  37. BIN
      agents/meshagent_aarch64-cortex-a53
  38. BIN
      agents/meshagent_android.apk
  39. BIN
      agents/meshagent_arm
  40. BIN
      agents/meshagent_arm-linaro
  41. BIN
      agents/meshagent_armhf
  42. BIN
      agents/meshagent_armhf2
  43. BIN
      agents/meshagent_freebsd_x86-64
  44. BIN
      agents/meshagent_linux-armada370-hf
  45. BIN
      agents/meshagent_mips
  46. BIN
      agents/meshagent_mips24kc
  47. BIN
      agents/meshagent_mipsel24kc
  48. BIN
      agents/meshagent_openbsd_x86-64
  49. BIN
      agents/meshagent_openwrt_x86_64
  50. BIN
      agents/meshagent_osx-arm-64
  51. BIN
      agents/meshagent_osx-universal-64
  52. BIN
      agents/meshagent_osx-x86-64
  53. BIN
      agents/meshagent_pogo
  54. BIN
      agents/meshagent_poky
  55. BIN
      agents/meshagent_poky64
  56. BIN
      agents/meshagent_riscv64
  57. BIN
      agents/meshagent_x86
  58. BIN
      agents/meshagent_x86-64
  59. BIN
      agents/meshagent_x86-64_nokvm
  60. BIN
      agents/meshagent_x86_nokvm
  61. 52 0
      agents/meshcmd.js
  62. 578 0
      agents/meshcore.js
  63. 206 0
      agents/meshcore_diagnostic.js
  64. 17 0
      agents/meshinstall-bsd-rcd.sh
  65. 84 0
      agents/meshinstall-initd.sh
  66. 361 0
      agents/meshinstall-linux.js
  67. 210 0
      agents/meshinstall-linux.sh
  68. 459 0
      agents/modules_meshcmd/amt-apfclient.js
  69. 610 0
      agents/modules_meshcmd/amt-ider.js
  70. 495 0
      agents/modules_meshcmd/amt-lme.js
  71. 499 0
      agents/modules_meshcmd/amt-mei.js
  72. 309 0
      agents/modules_meshcmd/amt-redir-duk.js
  73. 109 0
      agents/modules_meshcmd/amt-scanner.js
  74. 20 0
      agents/modules_meshcmd/amt-sol.js
  75. 141 0
      agents/modules_meshcmd/amt-wsman-duk.js
  76. 211 0
      agents/modules_meshcmd/amt-wsman.js
  77. 189 0
      agents/modules_meshcmd/amt-xml.js
  78. 1057 0
      agents/modules_meshcmd/amt.js
  79. 37 0
      agents/modules_meshcmd/linux-dhcp.js
  80. 359 0
      agents/modules_meshcmd/smbios.js
  81. 289 0
      agents/modules_meshcmd/sysinfo.js
  82. 101 0
      agents/modules_meshcmd/win-securitycenter.js
  83. 462 0
      agents/modules_meshcore/amt-apfclient.js
  84. 495 0
      agents/modules_meshcore/amt-lme.js
  85. 168 0
      agents/modules_meshcore/amt-manage.js
  86. 499 0
      agents/modules_meshcore/amt-mei.js
  87. 1037 0
      agents/modules_meshcore/computer-identifiers.js
  88. 290 0
      agents/modules_meshcore/coretranslations.json
  89. 37 0
      agents/modules_meshcore/linux-dhcp.js
  90. 335 0
      agents/modules_meshcore/monitor-border.js
  91. 359 0
      agents/modules_meshcore/smbios.js
  92. 325 0
      agents/modules_meshcore/sysinfo.js
  93. 195 0
      agents/modules_meshcore/util-agentlog.js
  94. 170 0
      agents/modules_meshcore/wifi-scanner-windows.js
  95. 127 0
      agents/modules_meshcore/wifi-scanner.js
  96. 163 0
      agents/modules_meshcore/win-console.js
  97. 194 0
      agents/modules_meshcore/win-deskutils.js
  98. 280 0
      agents/modules_meshcore/win-info.js
  99. 101 0
      agents/modules_meshcore/win-securitycenter.js
  100. 721 0
      agents/modules_meshcore/win-terminal.js

+ 14 - 0
.dockerignore

@@ -0,0 +1,14 @@
+/*.txt
+node_modules/
+.github/
+.vscode/
+docs/
+.git
+.gitignore
+.gitlab-ci.yml
+*.bat
+*.sln
+*.njsproj
+*.md
+examples
+tests

+ 1 - 0
.gitattributes

@@ -0,0 +1 @@
+*.sh		text eol=lf

+ 315 - 0
.gitignore

@@ -0,0 +1,315 @@
+## Ignore nodejs things
+[Nn]ode_modules/
+[Tt]yping/
+[Ii]mages-spare/
+[Dd]aemon/
+[Aa]gent/
+[Aa]gents/modules_meshcmd_min/
+[Aa]gents/modules_meshcore_min/
+[Aa]gents/meshcmd.min.js
+[Aa]gents/meshcore.min.js
+[Pp]ublic/translations/
+[Vv]iews/translations/
+[Ee]mails/translations/
+[Pp]ublic/*-min.htm
+[Vv]iews/*-min.handlebars
+meshcentral.db
+meshcentral.db.json
+mesherrors.txt
+bob.json
+.greenlockrc
+venv
+
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Typescript v1 declaration files
+typings/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+.DS_Store
+
+# When running mkdocs locally as dev
+docs/__pycache__/
+docs/env/
+docker-compose.yaml

+ 1 - 0
.npmrc

@@ -0,0 +1 @@
+engine-strict = true

+ 25 - 0
.vscode/launch.json

@@ -0,0 +1,25 @@
+{
+    // Verwendet IntelliSense zum Ermitteln möglicher Attribute.
+    // Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen.
+    // Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "pwa-chrome",
+            "request": "launch",
+            "name": "Launch Chrome against localhost",
+            "url": "http://localhost:8080",
+            "webRoot": "${workspaceFolder}"
+        },
+        {
+            "name": "Test docs locally with mkdocs",
+            "type": "debugpy",
+            "request": "launch",
+            "program": "${workspaceFolder}/docs/env/Scripts/mkdocs.exe",
+            "args": ["serve", "--dev-addr", "127.0.0.1:8010"],
+            "cwd": "${workspaceFolder}/docs",
+            "console": "integratedTerminal",
+            "preLaunchTask": "Start MkDocs Server"
+        }
+    ]
+}

+ 922 - 0
.vscode/settings.json

@@ -0,0 +1,922 @@
+{
+    "cSpell.words": [
+        "abcdf",
+        "accountchange",
+        "accountcreate",
+        "accountid",
+        "accountremove",
+        "acebase",
+        "acmd",
+        "acmepath",
+        "actiontype",
+        "adddevicegroup",
+        "adddeviceuser",
+        "adddomain",
+        "addmeshuser",
+        "addtousergroup",
+        "adduser",
+        "addusergroup",
+        "addusertodevice",
+        "addusertodevicegroup",
+        "addusertousergroup",
+        "adminaccount",
+        "adminname",
+        "agentaliasdns",
+        "agentaliasport",
+        "agentallowedip",
+        "agentapp",
+        "agentblockedip",
+        "agentconfig",
+        "agentconsole",
+        "agentcoredump",
+        "agentcoredumpusers",
+        "agentcustomization",
+        "agentdownload",
+        "agenterrorlogs",
+        "agentid",
+        "agentidletimeout",
+        "agentinfo",
+        "agentinvite",
+        "agentinvitecodes",
+        "agentkey",
+        "Agentless",
+        "agentnoproxy",
+        "agentport",
+        "agentportbind",
+        "agentporttls",
+        "agenttransfer",
+        "agenttype",
+        "agentupdateblocksize",
+        "agentupdatetest",
+        "agentwscompression",
+        "aliasport",
+        "allevents",
+        "allowaccountreset",
+        "allowframing",
+        "allowfullscreen",
+        "allowhighqualitydesktop",
+        "allowsavingdevicecredentials",
+        "allusers",
+        "alreadyinstalled",
+        "amtacmactivation",
+        "amtevents",
+        "amthost",
+        "amtmanager",
+        "amtoff",
+        "amton",
+        "amtonly",
+        "amtpass",
+        "amtreset",
+        "amtscanner",
+        "amtscanoptions",
+        "anewaccountcaptcha",
+        "apassword",
+        "apasswordhint",
+        "apikey",
+        "apos",
+        "appmetrics",
+        "apprelays",
+        "ashx",
+        "assistantconfig",
+        "assistantcustomization",
+        "assistantnoproxy",
+        "atag",
+        "authcookie",
+        "authenticode",
+        "authfail",
+        "authlog",
+        "authlogfile",
+        "Authn",
+        "authorizationurl",
+        "authstr",
+        "authstrategies",
+        "autofido",
+        "awsrds",
+        "backgroundcolor",
+        "backgroundonly",
+        "backupcode",
+        "backuppath",
+        "badargs",
+        "badtlscert",
+        "bancommonpasswords",
+        "batchupload",
+        "bitmask",
+        "Bounser",
+        "callbackurl",
+        "captchaargs",
+        "ccmp",
+        "Centralv",
+        "certbot",
+        "certfiles",
+        "certhash",
+        "certkeyhash",
+        "certpfx",
+        "certpfxpass",
+        "certurl",
+        "cfile",
+        "changedevice",
+        "changenode",
+        "changepassword",
+        "chatnotify",
+        "checkemail",
+        "checkmail",
+        "chnl",
+        "CIRA",
+        "ciraconn",
+        "ciralocalfqdn",
+        "ckey",
+        "clearpower",
+        "clientid",
+        "clientsecret",
+        "clipboardget",
+        "clipboardset",
+        "cmdoptions",
+        "cmds",
+        "cnonce",
+        "companyname",
+        "configfile",
+        "configfiles",
+        "configkey",
+        "connectionstring",
+        "Consts",
+        "cookieipcheck",
+        "cookiesamesite",
+        "coolofftime",
+        "coredump",
+        "coredumps",
+        "createaccount",
+        "createmesh",
+        "createusergroup",
+        "crowdsec",
+        "crypted",
+        "cscli",
+        "curloptionshttp",
+        "curloptionshttps",
+        "cuser",
+        "cuserid",
+        "customui",
+        "datafile",
+        "datapath",
+        "datas",
+        "datastr",
+        "dbconfig",
+        "dbdeleteconfigfiles",
+        "dbencryptkey",
+        "dbexport",
+        "dbexportmin",
+        "dbimport",
+        "dblistconfigfiles",
+        "dbmerge",
+        "dbpullconfigfiles",
+        "dbpulldatafiles",
+        "dbpushconfigfiles",
+        "dbshowconfigfile",
+        "debuglevel",
+        "defaultuserwebstate",
+        "deldump",
+        "deleteaccount",
+        "deletedefaultdomain",
+        "deletedomain",
+        "deletemesh",
+        "deleteuser",
+        "deleteusergroup",
+        "deluser",
+        "deluserpath",
+        "DESKLIMITEDINPUT",
+        "desktopmultiplex",
+        "desktopnotify",
+        "desktopprivacybar",
+        "desktopprompt",
+        "desktoprelays",
+        "desktopviewonly",
+        "devbox",
+        "devicefile",
+        "deviceid",
+        "deviceinfo",
+        "deviceinfocount",
+        "devicemessage",
+        "deviceopenurl",
+        "devicepower",
+        "devicepowerevents",
+        "devicesearchbarserverandclientname",
+        "deviceshare",
+        "devicesharing",
+        "devicetoast",
+        "devid",
+        "Digesthash",
+        "disablerequestedauthncontext",
+        "displayname",
+        "dlccore",
+        "dlcore",
+        "dldump",
+        "dnscount",
+        "dnssuffix",
+        "domaindefaults",
+        "domainid",
+        "domainname",
+        "domainurl",
+        "domainx",
+        "dont",
+        "dontlognull",
+        "downloadfile",
+        "dumpcores",
+        "dumpfile",
+        "editdevice",
+        "editdevicegroup",
+        "editgroup",
+        "editmesh",
+        "edituser",
+        "emailaddress",
+        "emailcheck",
+        "emaildomain",
+        "emailexists",
+        "emailok",
+        "emailvalidation",
+        "emailvalidationrequired",
+        "emailverified",
+        "entityid",
+        "entrypoints",
+        "errdesc",
+        "errlogpath",
+        "esversion",
+        "etype",
+        "eventlogger",
+        "exactport",
+        "exactports",
+        "exphbs",
+        "extractall",
+        "extrakey",
+        "extralinks",
+        "extrascriptsrc",
+        "factorauth",
+        "factorwarning",
+        "fadev",
+        "fahold",
+        "fasent",
+        "fastcert",
+        "fchallenge",
+        "fileaccess",
+        "filedata",
+        "filefullpath",
+        "filenotify",
+        "fileprompt",
+        "filesize",
+        "filespath",
+        "filestats",
+        "fileurl",
+        "filteredusers",
+        "filterid",
+        "firebaserelay",
+        "firstname",
+        "forceduserwebstate",
+        "foregroundcolor",
+        "forwardclient",
+        "forwardfor",
+        "forwardwrite",
+        "forwardwsocket",
+        "fpath",
+        "Freemonitoring",
+        "frontends",
+        "ftarget",
+        "fullpath",
+        "fullrights",
+        "fullscreen",
+        "gatewaymac",
+        "generateinvitelink",
+        "geourl",
+        "getnetworkinfo",
+        "getsysinfo",
+        "getwspass",
+        "googleusercontent",
+        "gotodevicename",
+        "gotonode",
+        "groupid",
+        "guestdevicesharing",
+        "guestname",
+        "GUESTSHARING",
+        "hashhex",
+        "Hashi",
+        "hashpass",
+        "hashpasssplit",
+        "hashpassword",
+        "Hashs",
+        "healthcheck",
+        "Hilaire",
+        "hkey",
+        "httpheaders",
+        "httplog",
+        "httpport",
+        "hwchallenge",
+        "hwotp",
+        "hwstate",
+        "hwtoken",
+        "Ider",
+        "idexists",
+        "idhex",
+        "idpurl",
+        "idsplit",
+        "iframe",
+        "ignoreagenthashcheck",
+        "iishash",
+        "imagebase",
+        "imagefile",
+        "indexagenterrorlog",
+        "indexmcrec",
+        "installflags",
+        "installsize",
+        "installtext",
+        "intelamt",
+        "interactiveonly",
+        "interuser",
+        "invitecodes",
+        "ipaddr",
+        "ipblockeduserredirect",
+        "ipcheck",
+        "ipex",
+        "ipkvm",
+        "iplayer",
+        "ipranges",
+        "isaml",
+        "Jitsi",
+        "jumpcloud",
+        "keyfile",
+        "keygrip",
+        "keyid",
+        "lanonly",
+        "LAPI",
+        "lastaddr",
+        "lastconnect",
+        "lastname",
+        "ldapauth",
+        "ldapobj",
+        "ldapoptions",
+        "ldapsaveusertofile",
+        "ldapsyncwithusergroups",
+        "ldapuserbinarykey",
+        "ldapuseremail",
+        "ldapusergroups",
+        "ldapuserimage",
+        "ldapuserkey",
+        "ldapusername",
+        "ldapuserphonenumber",
+        "ldapuserrealname",
+        "ldapuserrequiredgroupmembership",
+        "ldapusers",
+        "leok",
+        "letsencrypt",
+        "lightgray",
+        "limiteddesktop",
+        "limitedevents",
+        "LIMITEVENTS",
+        "Linaro",
+        "linuxpath",
+        "listdevicegroups",
+        "listdevices",
+        "listdomains",
+        "listevents",
+        "listusergroups",
+        "listuserids",
+        "listusers",
+        "listusersessions",
+        "listusersofdevicegroup",
+        "loadconfigfromdb",
+        "localdiscovery",
+        "localfile",
+        "localpath",
+        "localrelay",
+        "localsessionrecording",
+        "localurl",
+        "lockagentdownload",
+        "locksettings",
+        "logfile",
+        "logincodeb",
+        "logindomain",
+        "loginfooter",
+        "loginkey",
+        "loginkeyfile",
+        "loginlogo",
+        "loginmode",
+        "loginpass",
+        "loginpicture",
+        "loginscreen",
+        "logintoken",
+        "logintokengen",
+        "logintokenkey",
+        "logintokens",
+        "loginuser",
+        "logoback",
+        "logoutcontrols",
+        "logouturl",
+        "macrouter",
+        "magenturl",
+        "mailserver",
+        "mailtokengen",
+        "maintenancemode",
+        "mainwelcome",
+        "MANAGECOMPUTERS",
+        "managedevices",
+        "manageusers",
+        "markcoredump",
+        "maxfidokeys",
+        "maxlen",
+        "maxuseraccounts",
+        "mcpath",
+        "mcrdesktop",
+        "mcrec",
+        "mcrfiles",
+        "mcrouter",
+        "Mebx",
+        "meshaction",
+        "meshadmin",
+        "meshagent",
+        "meshagents",
+        "meshauth",
+        "meshcentral",
+        "meshcentralhost",
+        "meshchange",
+        "meshcmd",
+        "meshcommander",
+        "meshcookie",
+        "meshcore",
+        "meshctrl",
+        "meshdesktopmultiplex",
+        "meshdevicefile",
+        "mesherrorlogpath",
+        "mesherrors",
+        "meshfilename",
+        "meshid",
+        "meshidhex",
+        "meshidname",
+        "meshinstall",
+        "meshmail",
+        "meshmessenger",
+        "meshmessengerid",
+        "meshmessengerpicture",
+        "meshmessengertitle",
+        "meshname",
+        "meshosxagent",
+        "meshquota",
+        "meshrelay",
+        "MESHRIGHT",
+        "meshrights",
+        "meshscanner",
+        "meshserver",
+        "meshsettings",
+        "meshsettingslines",
+        "meshtype",
+        "meshuser",
+        "Messagebox",
+        "messageid",
+        "Messenging",
+        "minfo",
+        "minifyall",
+        "minifycore",
+        "mongodbcol",
+        "mongodump",
+        "mongorestore",
+        "moutput",
+        "movetodevicegroup",
+        "mpkg",
+        "mpsaliasport",
+        "mpscert",
+        "mpsdebug",
+        "mpspass",
+        "mpsport",
+        "mpsserver",
+        "mpsservers",
+        "MPSSSL",
+        "mpstlsoffload",
+        "mqttbroker",
+        "MSCHA",
+        "msgid",
+        "mstsc",
+        "mstscrelay",
+        "mtype",
+        "multiplexor",
+        "multiresponse",
+        "multivalued",
+        "myaccountname",
+        "mycompany",
+        "mydomain",
+        "mypassword",
+        "myserver",
+        "myservername",
+        "nameexists",
+        "nedbtodb",
+        "netif",
+        "newaccountemaildomains",
+        "newaccountname",
+        "newaccountrealms",
+        "newaccounts",
+        "newaccountscaptcha",
+        "newaccountspass",
+        "newaccountsrights",
+        "newaccountsusergroups",
+        "newgroupname",
+        "newobj",
+        "newpass",
+        "newpassword",
+        "NGNIX",
+        "nightmode",
+        "noact",
+        "noagentupdate",
+        "noamt",
+        "noauth",
+        "noav",
+        "nodeconnect",
+        "nodecount",
+        "nodeid",
+        "nodeids",
+        "nodeidsplit",
+        "nodeinfo",
+        "nodekey",
+        "nodepath",
+        "NODESKTOP",
+        "nodewindows",
+        "nofiles",
+        "nofirewall",
+        "nolog",
+        "nologout",
+        "NOMESHCMD",
+        "nominify",
+        "nonalpha",
+        "NONEWDEVICES",
+        "nonewgroups",
+        "noproxy",
+        "noredirect",
+        "nosniff",
+        "noterminal",
+        "notools",
+        "nouser",
+        "nousers",
+        "novnc",
+        "npmjs",
+        "npmpath",
+        "npmproxy",
+        "npmtag",
+        "objid",
+        "ODELAY",
+        "offloader",
+        "offloaders",
+        "oidc",
+        "oldpassword",
+        "oldpasswordban",
+        "oldpasswords",
+        "oneclickrecovery",
+        "onlyselecteddevicegroups",
+        "onlyselectedusers",
+        "openidconnect",
+        "openstreetmap",
+        "openurl",
+        "orphanagentuser",
+        "osdesc",
+        "osinfo",
+        "otpdev",
+        "otpekey",
+        "otpemail",
+        "otphkeys",
+        "otpkeys",
+        "otplib",
+        "otppush",
+        "otpsecret",
+        "otpsms",
+        "parentpath",
+        "passchange",
+        "passhint",
+        "passlogin",
+        "passrequirementstr",
+        "passtype",
+        "passwordrequirements",
+        "passwordrequirementsstr",
+        "pastlogin",
+        "pathx",
+        "peinfo",
+        "phonenumber",
+        "PKCK",
+        "plivo",
+        "pluginadmin",
+        "plusplus",
+        "portbind",
+        "postflight",
+        "poweraction",
+        "powerevents",
+        "Preconfigured",
+        "Proto",
+        "publicid",
+        "pushlogin",
+        "pushrelay",
+        "pushrelayserver",
+        "qport",
+        "randompass",
+        "Raritan",
+        "rauth",
+        "rawdata",
+        "rcookie",
+        "rdpport",
+        "realname",
+        "recordencryptionrecode",
+        "recordpath",
+        "redir",
+        "rediraliasport",
+        "redirections",
+        "redirport",
+        "redirserver",
+        "refreshtoken",
+        "relayaliasport",
+        "relaydns",
+        "relayid",
+        "relayport",
+        "relayserver",
+        "relaysession",
+        "remembertoken",
+        "remoteaddr",
+        "remoteaddrport",
+        "REMOTECOMMAND",
+        "remotecontrol",
+        "remotefile",
+        "remotepath",
+        "REMOTEVIEWONLY",
+        "removeallusersfromusergroup",
+        "removedevicegroup",
+        "removedomain",
+        "removefromdomain",
+        "removefromusergroup",
+        "removemeshuser",
+        "removesubdomain",
+        "removetestagents",
+        "removeuser",
+        "removeuserfromdevice",
+        "removeuserfromdevicegroup",
+        "removeuserfromusergroup",
+        "removeusergroup",
+        "resetaccount",
+        "RESETOFF",
+        "resetpass",
+        "responseid",
+        "restoreserver",
+        "rightsstr",
+        "rname",
+        "rnamel",
+        "rootcert",
+        "rootredirect",
+        "rpassword",
+        "rpasswordhint",
+        "rport",
+        "rtpass",
+        "rtuser",
+        "runas",
+        "runasuser",
+        "runasuseronly",
+        "runcommand",
+        "runcommands",
+        "runmode",
+        "runonservererror",
+        "runonserverupdated",
+        "ruserid",
+        "sameorigin",
+        "selfupdate",
+        "selfurl",
+        "senderid",
+        "sendgrid",
+        "sendinviteemail",
+        "serialtunnel",
+        "SERVERBACKUP",
+        "serverfeatures",
+        "serverfiles",
+        "serverhttps",
+        "serverid",
+        "serveridhex",
+        "serverinfo",
+        "serverkey",
+        "servername",
+        "servernoproxy",
+        "serverpath",
+        "serverpic",
+        "serverport",
+        "SERVERRESTORE",
+        "servertlshash",
+        "serverupdate",
+        "servicename",
+        "servicepath",
+        "sessioncode",
+        "sessionkey",
+        "sessionrecording",
+        "sessionsamesite",
+        "sessiontime",
+        "setbad",
+        "SETNOTES",
+        "settodomain",
+        "sftpconnect",
+        "shareid",
+        "showagents",
+        "showall",
+        "showallmeshes",
+        "showevents",
+        "showiplocations",
+        "showitem",
+        "showmeshes",
+        "shownodes",
+        "showpasswordlogin",
+        "showpower",
+        "showsmbios",
+        "showusergroups",
+        "showusers",
+        "showversion",
+        "siteadmin",
+        "SITERIGHT",
+        "sitestyle",
+        "smsserver",
+        "specificupdate",
+        "splitip",
+        "splitpath",
+        "spliturl",
+        "srights",
+        "sshconnect",
+        "sshfilesrelay",
+        "sshport",
+        "sshrelay",
+        "sshterminalrelay",
+        "ssid",
+        "sspi",
+        "startack",
+        "statsevents",
+        "stricttransportsecurity",
+        "Strs",
+        "subdir",
+        "swarmallowedip",
+        "swarmport",
+        "swarmserver",
+        "sysinfo",
+        "syslogauth",
+        "syslogjson",
+        "syslogtcp",
+        "tcpport",
+        "telnyx",
+        "temail",
+        "tenantid",
+        "terminalnotify",
+        "terminalprompt",
+        "termsize",
+        "timedoc",
+        "titleid",
+        "titlepicture",
+        "tkip",
+        "tlscertcheck",
+        "tlshash",
+        "tlsock",
+        "tlsoffload",
+        "tlsoptions",
+        "tlsrootcert",
+        "tlsstrict",
+        "tmpdl",
+        "tokenemail",
+        "tokenlogin",
+        "tokenpassword",
+        "tokenpush",
+        "tokenrequired",
+        "tokensms",
+        "tokenurl",
+        "tokenuserid",
+        "tokenusername",
+        "totalsize",
+        "TOTP",
+        "tpass",
+        "tpassword",
+        "tpush",
+        "traefik",
+        "translateall",
+        "translationpath",
+        "trustedcert",
+        "trustedproxy",
+        "tsms",
+        "TTLS",
+        "tunnelws",
+        "tunnelwsstate",
+        "tuser",
+        "tuserid",
+        "tusername",
+        "twofactor",
+        "twofactorcookiedurationdays",
+        "twofactortimeout",
+        "tzoffset",
+        "uaparser",
+        "ucookie",
+        "ugroup",
+        "ugroups",
+        "ugrp",
+        "ugrpid",
+        "uicustomevent",
+        "unadmin",
+        "unknownuserrootredirect",
+        "unsealkey",
+        "updatefiles",
+        "uploadack",
+        "uploaderror",
+        "uploadfile",
+        "uploadfilebatch",
+        "uploadmeshcorefile",
+        "uploadstart",
+        "urlpath",
+        "urlswitching",
+        "useid",
+        "userallowedip",
+        "userblockedip",
+        "userbroadcast",
+        "userconsentflags",
+        "usercount",
+        "userex",
+        "userfiles",
+        "userfirst",
+        "usergroupchange",
+        "usergroups",
+        "userid",
+        "userids",
+        "userimage",
+        "userinfourl",
+        "usernameisemail",
+        "userquota",
+        "userrequiredhttpheader",
+        "Usersessionidletimeout",
+        "usersid",
+        "usersplit",
+        "vaultdeleteconfigfiles",
+        "vaultpullconfigfiles",
+        "vaultpushconfigfiles",
+        "verifyemail",
+        "Viewmode",
+        "viewonly",
+        "WAKEDEVICE",
+        "wakedevices",
+        "Walkthru",
+        "wanonly",
+        "Webauthn",
+        "webcerthash",
+        "webdefault",
+        "webemailspath",
+        "webider",
+        "webpublicpath",
+        "webpush",
+        "webrelay",
+        "webrelaydata",
+        "webrelayserver",
+        "webrequest",
+        "webrtc",
+        "webrtconfig",
+        "webserver",
+        "websockets",
+        "WEBSSL",
+        "webstate",
+        "webviewspath",
+        "WELCOMEMSG",
+        "welcomepicture",
+        "welcomepicturefullscreen",
+        "welcometext",
+        "wgetoptionshttp",
+        "wgetoptionshttps",
+        "wildleek",
+        "winassistant",
+        "winpath",
+        "winrouter",
+        "winservice",
+        "wsagents",
+        "wscompression",
+        "wsrelays",
+        "wssessioncount",
+        "wssessions",
+        "xarg",
+        "xbytes",
+        "xcmd",
+        "xdomain",
+        "xdomains",
+        "xenv",
+        "xevents",
+        "xfile",
+        "xfilelen",
+        "xfilepath",
+        "xflags",
+        "xforwardedhost",
+        "xinstall",
+        "xjslint",
+        "xmeshes",
+        "xpad",
+        "xpassword",
+        "xrelay",
+        "xrestart",
+        "xstate",
+        "xtls",
+        "xtransport",
+        "xuninstall",
+        "xuserid",
+        "xusername",
+        "xxdata",
+        "xxprocess",
+        "xxurl",
+        "xxuser",
+        "xxxprocess",
+        "Ylian",
+        "yubikey",
+        "yubikeyotp",
+        "zdata",
+        "zipfile"
+    ]
+}

+ 60 - 0
.vscode/tasks.json

@@ -0,0 +1,60 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "Setup Python Environment",
+            "type": "shell",
+            "command": "python",
+            "args": ["-m", "venv", "env"],
+            "options": {
+                "cwd": "${workspaceFolder}/docs"
+            },
+            "group": "build",
+            "presentation": {
+                "echo": true,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared"
+            },
+            "problemMatcher": []
+        },
+        {
+            "label": "Install MkDocs Requirements",
+            "type": "shell",
+            "command": ".\\env\\Scripts\\activate.ps1; python -m pip install --upgrade pip; pip install -r requirements.txt",
+            "options": {
+                "cwd": "${workspaceFolder}/docs"
+            },
+            "group": "build",
+            "presentation": {
+                "echo": true,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared"
+            },
+            "problemMatcher": [],
+            "dependsOn": "Setup Python Environment"
+        },
+        {
+            "label": "Start MkDocs Server",
+            "type": "shell",
+            "command": ".\\env\\Scripts\\activate.ps1; Start-Process http://localhost:8010; mkdocs serve",
+            "options": {
+                "cwd": "${workspaceFolder}/docs"
+            },
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            },
+            "presentation": {
+                "echo": true,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared"
+            },
+            "problemMatcher": [],
+            "isBackground": true,
+            "dependsOn": "Install MkDocs Requirements"
+        }
+    ]
+}

+ 65 - 0
CODE_OF_CONDUCT.md

@@ -0,0 +1,65 @@
+# MeshCentral Community Standards
+
+## Purpose
+The MeshCentral Community is composed of professionals and volunteers from around the world, working on development, support, mentorship, and community engagement. Our strength lies in our diversity, and our goal is to create a collaborative, welcoming, and technically focused environment. This code of conduct applies to all MeshCentral-managed spaces, including GitHub, Discord, Telegram, Reddit, forums, and any other community communication channels, and it must be prominently available across all these platforms to ensure visibility, consistency, and community alignment. Moderators and all community members are subject to these standards.
+
+We should focus on welcoming anyone who wants to contribute, fostering engagement, and supporting community-driven improvements. Treat this code as a guide to make the community productive, inclusive, and enriching for everyone.
+
+## Be Friendly and Welcoming
+- Strive to make everyone feel included, supported, and valued.
+- Be patient and understanding, particularly when communicating with members for whom English may not be their first language.
+- Foster mentorship, guidance, and constructive collaboration whenever possible.
+
+## Conduct
+- Critique ideas, not individuals.
+- Defamatory, derogatory, or demeaning speech is prohibited.
+- Avoid personal attacks, harassment, or hostile behavior.
+- Keep discussions constructive, respectful, and technically oriented.
+- Choose words carefully and communicate professionally.
+- Repeated harassment or ignoring a request to stop is unacceptable.
+
+## Collaboration
+- Welcome contributors of all experience levels.
+- Prioritize community benefit over personal preferences or private agendas.
+- Maintain openness to alternative solutions and approaches.
+- Focus on engagement, community-driven improvements, and supporting contributors who want to participate actively.
+- Resolve disagreements constructively, seeking to understand differing viewpoints. Mistakes happen; focus on learning and improving rather than assigning blame.
+
+## Technical Rigor
+- Share information that is accurate, verifiable, or based on direct experience.
+- State clearly when uncertain or speculating.
+- Encourage constructive correction and peer review without hostility.
+- Consider the consequences of your work on other community members and users.
+
+## Privacy and Security
+- Do not share credentials, private data, or screenshots/logs containing sensitive information.
+- Do not request or provide assistance with unauthorized access, illegal activity, or unsafe configurations.
+
+## Contribution Etiquette
+- When opening issues, provide clear context, reproducible steps, and relevant environment details.
+- When proposing enhancements, remain receptive to critique and alternatives.
+- Respect project maintainers, scope boundaries, and development priorities.
+- Strive to provide helpful and actionable feedback to others in a constructive manner.
+
+## How to Ask for Help Effectively
+To receive accurate and useful support:
+1. State your objective clearly.
+2. Provide environment details (OS, MeshCentral version, hosting setup, network context).
+3. Include reproducible steps and error messages (with sensitive data removed).
+4. Describe what has already been attempted.
+5. Ask specific, focused questions.
+6. Keep your message structured and concise.
+
+## Moderation
+- Moderators across all community platforms are subject to this code.
+- Moderators may remove content that violates these standards.
+- Repeated violations may result in warnings, restricted participation, or removal from community spaces.
+- Moderation actions must be applied consistently and impartially to maintain order and a positive environment.
+- Moderators are encouraged to model friendliness, patience, and inclusivity.
+
+## Language
+- Use clear and comprehensible English.
+- Maintain a professional, kind, and respectful tone at all times.
+
+## Enforcement
+Violations of these standards may result in warnings, content removal, or removal from community spaces. Severe or repeated violations may result in permanent exclusion. Members are encouraged to report any violations to moderators or directly to project maintainers. This code should

+ 2 - 0
CreateSourcePackage.bat

@@ -0,0 +1,2 @@
+del MeshCentral2.zip
+"C:\Program Files\WinRAR\WinRAR.exe" a MeshCentral2.zip @SourceFileList.txt

+ 201 - 0
LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2017-2025 Intel Corporation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 22 - 0
MeshCentral.sln

@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.23107.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "MeshCentralServer", "MeshCentralServer.njsproj", "{00C71E60-81CC-4B15-B486-10D27935D7EB}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{00C71E60-81CC-4B15-B486-10D27935D7EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{00C71E60-81CC-4B15-B486-10D27935D7EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{00C71E60-81CC-4B15-B486-10D27935D7EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{00C71E60-81CC-4B15-B486-10D27935D7EB}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 817 - 0
MeshCentralServer.njsproj

@@ -0,0 +1,817 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{00c71e60-81cc-4b15-b486-10d27935d7eb}</ProjectGuid>
+    <ProjectHome />
+    <ProjectView>ShowAllFiles</ProjectView>
+    <StartupFile>meshcentral.js</StartupFile>
+    <WorkingDirectory>.</WorkingDirectory>
+    <OutputPath>.</OutputPath>
+    <ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+    <Name>MeshCentralServer</Name>
+    <StartWebBrowser>False</StartWebBrowser>
+    <ScriptArguments>
+    </ScriptArguments>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)' == 'Debug'" />
+  <PropertyGroup Condition="'$(Configuration)' == 'Release'" />
+  <ItemGroup>
+    <Compile Include="agents\meshcmd.js" />
+    <Compile Include="agents\meshcmd.min.js" />
+    <Compile Include="agents\meshcore.js" />
+    <Compile Include="agents\meshcore.min.js" />
+    <Compile Include="agents\meshinstall-linux.js" />
+    <Compile Include="agents\modules_meshcmd\amt-apfclient.js" />
+    <Compile Include="agents\modules_meshcmd\amt-ider.js" />
+    <Compile Include="agents\modules_meshcmd\amt-lme.js" />
+    <Compile Include="agents\modules_meshcmd\amt-mei.js" />
+    <Compile Include="agents\modules_meshcmd\amt-redir-duk.js" />
+    <Compile Include="agents\modules_meshcmd\amt-scanner.js" />
+    <Compile Include="agents\modules_meshcmd\amt-wsman-duk.js" />
+    <Compile Include="agents\modules_meshcmd\amt-wsman.js" />
+    <Compile Include="agents\modules_meshcmd\amt-xml.js" />
+    <Compile Include="agents\modules_meshcmd\amt.js" />
+    <Compile Include="agents\modules_meshcmd\linux-dhcp.js" />
+    <Compile Include="agents\modules_meshcmd\smbios.js" />
+    <Compile Include="agents\modules_meshcmd\sysinfo.js" />
+    <Compile Include="agents\modules_meshcmd\win-securitycenter.js" />
+    <Compile Include="agents\modules_meshcmd_min\amt-ider.min.js" />
+    <Compile Include="agents\modules_meshcmd_min\amt-lme.min.js" />
+    <Compile Include="agents\modules_meshcmd_min\amt-mei.min.js" />
+    <Compile Include="agents\modules_meshcmd_min\amt-redir-duk.min.js" />
+    <Compile Include="agents\modules_meshcmd_min\amt-scanner.min.js" />
+    <Compile Include="agents\modules_meshcmd_min\amt-script.min.js" />
+    <Compile Include="agents\modules_meshcmd_min\amt-wsman-duk.min.js" />
+    <Compile Include="agents\modules_meshcmd_min\amt-wsman.min.js" />
+    <Compile Include="agents\modules_meshcmd_min\amt-xml.min.js" />
+    <Compile Include="agents\modules_meshcmd_min\amt.min.js" />
+    <Compile Include="agents\modules_meshcmd_min\service-host.min.js" />
+    <Compile Include="agents\modules_meshcmd_min\service-manager.min.js" />
+    <Compile Include="agents\modules_meshcmd_min\smbios.min.js" />
+    <Compile Include="agents\modules_meshcmd_min\user-sessions.min.js" />
+    <Compile Include="agents\modules_meshcore\amt-apfclient.js" />
+    <Compile Include="agents\modules_meshcore\amt-lme.js" />
+    <Compile Include="agents\modules_meshcore\amt-manage.js" />
+    <Compile Include="agents\modules_meshcore\amt-mei.js" />
+    <Compile Include="agents\modules_meshcore\linux-dhcp.js" />
+    <Compile Include="agents\modules_meshcore\monitor-border.js" />
+    <Compile Include="agents\modules_meshcore\smbios.js" />
+    <Compile Include="agents\modules_meshcore\sysinfo.js" />
+    <Compile Include="agents\modules_meshcore\util-agentlog.js" />
+    <Compile Include="agents\modules_meshcore\wifi-scanner-windows.js" />
+    <Compile Include="agents\modules_meshcore\wifi-scanner.js" />
+    <Compile Include="agents\modules_meshcore\win-console.js" />
+    <Compile Include="agents\modules_meshcore\win-info.js" />
+    <Compile Include="agents\modules_meshcore\win-securitycenter.js" />
+    <Compile Include="agents\modules_meshcore\win-terminal.js" />
+    <Compile Include="agents\modules_meshcore\win-virtual-terminal.js" />
+    <Compile Include="agents\modules_meshcore_min\amt-lme.min.js" />
+    <Compile Include="agents\modules_meshcore_min\amt-mei.min.js" />
+    <Compile Include="agents\modules_meshcore_min\amt-scanner.min.js" />
+    <Compile Include="agents\modules_meshcore_min\amt-wsman-duk.min.js" />
+    <Compile Include="agents\modules_meshcore_min\amt-wsman.min.js" />
+    <Compile Include="agents\modules_meshcore_min\amt-xml.min.js" />
+    <Compile Include="agents\modules_meshcore_min\amt.min.js" />
+    <Compile Include="agents\modules_meshcore_min\linux-dbus.min.js" />
+    <Compile Include="agents\modules_meshcore_min\monitor-border.min.js" />
+    <Compile Include="agents\modules_meshcore_min\power-monitor.min.js" />
+    <Compile Include="agents\modules_meshcore_min\smbios.min.js" />
+    <Compile Include="agents\modules_meshcore_min\wifi-scanner-windows.min.js" />
+    <Compile Include="agents\modules_meshcore_min\wifi-scanner.min.js" />
+    <Compile Include="agents\modules_meshcore_min\win-console.min.js" />
+    <Compile Include="agents\modules_meshcore_min\win-terminal.min.js" />
+    <Compile Include="agents\recoverycore.js" />
+    <Compile Include="agents\testsuite.js" />
+    <Compile Include="agents\tinycore.js" />
+    <Compile Include="amt-ider.js" />
+    <Compile Include="amtevents.js" />
+    <Compile Include="amtmanager.js" />
+    <Compile Include="amtscanner.js" />
+    <Compile Include="amtscript.js" />
+    <Compile Include="amt\amt-ider-module.js" />
+    <Compile Include="amt\amt-ider.js" />
+    <Compile Include="amt\amt-redir-mesh.js" />
+    <Compile Include="amt\amt-setupbin.js" />
+    <Compile Include="amt\amt-wsman-comm.js" />
+    <Compile Include="amt\amt-wsman.js" />
+    <Compile Include="amt\amt-xml.js" />
+    <Compile Include="amt\amt.js" />
+    <Compile Include="authenticode.js" />
+    <Compile Include="crowdsec.js" />
+    <Compile Include="exeHandler.js" />
+    <Compile Include="amtprovisioningserver.js" />
+    <Compile Include="firebase.js" />
+    <Compile Include="letsencrypt.js" />
+    <Compile Include="mcrec.js" />
+    <Compile Include="meshaccelerator.js" />
+    <Compile Include="meshbot.js" />
+    <Compile Include="meshctrl.js" />
+    <Compile Include="meshdesktopmultiplex.js" />
+    <Compile Include="meshipkvm.js" />
+    <Compile Include="meshmail.js" />
+    <Compile Include="meshmessaging.js" />
+    <Compile Include="meshrelay.js" />
+    <Compile Include="meshsms.js" />
+    <Compile Include="meshscanner.js" />
+    <Compile Include="certoperations.js" />
+    <Compile Include="common.js" />
+    <Compile Include="db.js" />
+    <Compile Include="interceptor.js" />
+    <Compile Include="meshcentral.js" />
+    <Compile Include="meshagent.js" />
+    <Compile Include="meshdevicefile.js" />
+    <Compile Include="meshuser.js" />
+    <Compile Include="monitoring.js" />
+    <Compile Include="mpsserver.js" />
+    <Compile Include="mqttbroker.js" />
+    <Compile Include="apprelays.js" />
+    <Compile Include="pkcs7-modified.js" />
+    <Compile Include="pluginHandler.js" />
+    <Compile Include="public\mstsc\client.js" />
+    <Compile Include="public\mstsc\js\keyboard.js" />
+    <Compile Include="public\mstsc\js\mstsc.js" />
+    <Compile Include="public\mstsc\js\rle.js" />
+    <Compile Include="public\novnc\app\error-handler.js" />
+    <Compile Include="public\novnc\app\localization.js" />
+    <Compile Include="public\novnc\app\ui.js" />
+    <Compile Include="public\novnc\app\webutil.js" />
+    <Compile Include="public\novnc\core\base64.js" />
+    <Compile Include="public\novnc\core\decoders\copyrect.js" />
+    <Compile Include="public\novnc\core\decoders\hextile.js" />
+    <Compile Include="public\novnc\core\decoders\raw.js" />
+    <Compile Include="public\novnc\core\decoders\rre.js" />
+    <Compile Include="public\novnc\core\decoders\tight.js" />
+    <Compile Include="public\novnc\core\decoders\tightpng.js" />
+    <Compile Include="public\novnc\core\des.js" />
+    <Compile Include="public\novnc\core\display.js" />
+    <Compile Include="public\novnc\core\encodings.js" />
+    <Compile Include="public\novnc\core\inflator.js" />
+    <Compile Include="public\novnc\core\input\domkeytable.js" />
+    <Compile Include="public\novnc\core\input\fixedkeys.js" />
+    <Compile Include="public\novnc\core\input\keyboard.js" />
+    <Compile Include="public\novnc\core\input\keysym.js" />
+    <Compile Include="public\novnc\core\input\keysymdef.js" />
+    <Compile Include="public\novnc\core\input\mouse.js" />
+    <Compile Include="public\novnc\core\input\util.js" />
+    <Compile Include="public\novnc\core\input\vkeys.js" />
+    <Compile Include="public\novnc\core\input\xtscancodes.js" />
+    <Compile Include="public\novnc\core\rfb.js" />
+    <Compile Include="public\novnc\core\util\browser.js" />
+    <Compile Include="public\novnc\core\util\cursor.js" />
+    <Compile Include="public\novnc\core\util\events.js" />
+    <Compile Include="public\novnc\core\util\eventtarget.js" />
+    <Compile Include="public\novnc\core\util\logging.js" />
+    <Compile Include="public\novnc\core\util\polyfill.js" />
+    <Compile Include="public\novnc\core\util\strings.js" />
+    <Compile Include="public\novnc\core\websock.js" />
+    <Compile Include="public\novnc\vendor\browser-es-module-loader\dist\babel-worker.js" />
+    <Compile Include="public\novnc\vendor\browser-es-module-loader\dist\browser-es-module-loader.js" />
+    <Compile Include="public\novnc\vendor\browser-es-module-loader\rollup.config.js" />
+    <Compile Include="public\novnc\vendor\browser-es-module-loader\src\babel-worker.js" />
+    <Compile Include="public\novnc\vendor\browser-es-module-loader\src\browser-es-module-loader.js" />
+    <Compile Include="public\novnc\vendor\pako\lib\utils\common.js" />
+    <Compile Include="public\novnc\vendor\pako\lib\zlib\adler32.js" />
+    <Compile Include="public\novnc\vendor\pako\lib\zlib\constants.js" />
+    <Compile Include="public\novnc\vendor\pako\lib\zlib\crc32.js" />
+    <Compile Include="public\novnc\vendor\pako\lib\zlib\deflate.js" />
+    <Compile Include="public\novnc\vendor\pako\lib\zlib\gzheader.js" />
+    <Compile Include="public\novnc\vendor\pako\lib\zlib\inffast.js" />
+    <Compile Include="public\novnc\vendor\pako\lib\zlib\inflate.js" />
+    <Compile Include="public\novnc\vendor\pako\lib\zlib\inftrees.js" />
+    <Compile Include="public\novnc\vendor\pako\lib\zlib\messages.js" />
+    <Compile Include="public\novnc\vendor\pako\lib\zlib\trees.js" />
+    <Compile Include="public\novnc\vendor\pako\lib\zlib\zstream.js" />
+    <Compile Include="public\novnc\vendor\promise.js" />
+    <Compile Include="public\scripts\agent-rdp-0.0.1.js" />
+    <Compile Include="public\scripts\agent-redir-rtc-0.1.0.js" />
+    <Compile Include="public\serviceworker.js" />
+    <Compile Include="rdp\asn1\ber.js" />
+    <Compile Include="rdp\asn1\index.js" />
+    <Compile Include="rdp\asn1\spec.js" />
+    <Compile Include="rdp\asn1\univ.js" />
+    <Compile Include="rdp\core\error.js" />
+    <Compile Include="rdp\core\index.js" />
+    <Compile Include="rdp\core\layer.js" />
+    <Compile Include="rdp\core\log.js" />
+    <Compile Include="rdp\core\rle.js" />
+    <Compile Include="rdp\core\type.js" />
+    <Compile Include="rdp\index.js" />
+    <Compile Include="rdp\protocol\cert.js" />
+    <Compile Include="rdp\protocol\index.js" />
+    <Compile Include="rdp\protocol\nla.js" />
+    <Compile Include="rdp\protocol\pdu\caps.js" />
+    <Compile Include="rdp\protocol\pdu\cliprdr.js" />
+    <Compile Include="rdp\protocol\pdu\data.js" />
+    <Compile Include="rdp\protocol\pdu\global.js" />
+    <Compile Include="rdp\protocol\pdu\index.js" />
+    <Compile Include="rdp\protocol\pdu\lic.js" />
+    <Compile Include="rdp\protocol\pdu\sec.js" />
+    <Compile Include="rdp\protocol\rdp.js" />
+    <Compile Include="rdp\protocol\t125\gcc.js" />
+    <Compile Include="rdp\protocol\t125\index.js" />
+    <Compile Include="rdp\protocol\t125\mcs.js" />
+    <Compile Include="rdp\protocol\t125\per.js" />
+    <Compile Include="rdp\protocol\tpkt.js" />
+    <Compile Include="rdp\protocol\x224.js" />
+    <Compile Include="rdp\security\index.js" />
+    <Compile Include="rdp\security\jsbn.js" />
+    <Compile Include="rdp\security\md4.js" />
+    <Compile Include="rdp\security\rc4.js" />
+    <Compile Include="rdp\security\rsa.js" />
+    <Compile Include="rdp\security\x509.js" />
+    <Compile Include="swarmserver.js" />
+    <Compile Include="multiserver.js" />
+    <Compile Include="pass.js" />
+    <Compile Include="public\scripts\amt-0.2.0.js" />
+    <Compile Include="public\scripts\agent-desktop-0.0.2.js" />
+    <Compile Include="public\scripts\amt-desktop-0.0.2.js" />
+    <Compile Include="public\scripts\amt-ider-ws-0.0.1.js" />
+    <Compile Include="public\scripts\agent-redir-ws-0.1.1.js" />
+    <Compile Include="public\scripts\amt-redir-ws-0.1.0.js" />
+    <Compile Include="public\scripts\amt-script-0.2.0.js" />
+    <Compile Include="public\scripts\amt-setupbin-0.1.0.js" />
+    <Compile Include="public\scripts\amt-terminal-0.0.2.js" />
+    <Compile Include="public\scripts\amt-wsman-0.2.0.js" />
+    <Compile Include="public\scripts\amt-wsman-ws-0.2.0.js" />
+    <Compile Include="public\scripts\common-0.0.1.js" />
+    <Compile Include="public\scripts\meshcentral.js" />
+    <Compile Include="redirserver.js" />
+    <Compile Include="taskmanager.js" />
+    <Compile Include="translate\translate.js" />
+    <Compile Include="webauthn.js" />
+    <Compile Include="webrelayserver.js" />
+    <Compile Include="webserver.js" />
+    <Compile Include="winservice.js" />
+    <Content Include="agents\codesign.cer" />
+    <Content Include="agents\compressModules.bat" />
+    <Content Include="agents\MeshAgentOSXPackager.zip" />
+    <Content Include="agents\meshagent_arm" />
+    <Content Include="agents\meshagent_armhf" />
+    <Content Include="agents\meshagent_mips" />
+    <Content Include="agents\meshagent_osx-x86-64" />
+    <Content Include="agents\meshagent_pogo" />
+    <Content Include="agents\meshagent_poky" />
+    <Content Include="agents\meshagent_poky64" />
+    <Content Include="agents\meshagent_x86" />
+    <Content Include="agents\meshagent_x86-64" />
+    <Content Include="agents\meshagent_x86-64_nokvm" />
+    <Content Include="agents\meshagent_x86_nokvm" />
+    <Content Include="agents\MeshCentralRouter.exe" />
+    <Content Include="agents\MeshCmd-signed.exe" />
+    <Content Include="agents\MeshCmd64-signed.exe" />
+    <Content Include="agents\MeshCommander-Small.gz" />
+    <Content Include="agents\meshinstall-initd.sh" />
+    <Content Include="agents\meshinstall-linux.sh" />
+    <Content Include="agents\MeshService.exe" />
+    <Content Include="agents\MeshService.pdb" />
+    <Content Include="agents\MeshService64.exe" />
+    <Content Include="agents\MeshService64.pdb" />
+    <Content Include="agents\modules_meshcore\coretranslations.json" />
+    <Content Include="CreateSourcePackage.bat" />
+    <Content Include="dependencies.txt" />
+    <Content Include="emails\account-check.html" />
+    <Content Include="emails\account-check.txt" />
+    <Content Include="emails\account-invite.html" />
+    <Content Include="emails\account-invite.txt" />
+    <Content Include="emails\account-login.html" />
+    <Content Include="emails\account-login.txt" />
+    <Content Include="emails\account-reset.html" />
+    <Content Include="emails\account-reset.txt" />
+    <Content Include="emails\mesh-invite.html" />
+    <Content Include="emails\mesh-invite.txt" />
+    <Content Include="emails\sms-messages.txt" />
+    <Content Include="emails\translations\account-check_cs.html" />
+    <Content Include="emails\translations\account-check_cs.txt" />
+    <Content Include="emails\translations\account-check_de.html" />
+    <Content Include="emails\translations\account-check_de.txt" />
+    <Content Include="emails\translations\account-check_es.html" />
+    <Content Include="emails\translations\account-check_es.txt" />
+    <Content Include="emails\translations\account-check_fi.html" />
+    <Content Include="emails\translations\account-check_fi.txt" />
+    <Content Include="emails\translations\account-check_fr.html" />
+    <Content Include="emails\translations\account-check_fr.txt" />
+    <Content Include="emails\translations\account-check_hi.html" />
+    <Content Include="emails\translations\account-check_hi.txt" />
+    <Content Include="emails\translations\account-check_ja.html" />
+    <Content Include="emails\translations\account-check_ja.txt" />
+    <Content Include="emails\translations\account-check_ko.html" />
+    <Content Include="emails\translations\account-check_ko.txt" />
+    <Content Include="emails\translations\account-check_nl.html" />
+    <Content Include="emails\translations\account-check_nl.txt" />
+    <Content Include="emails\translations\account-check_pt.html" />
+    <Content Include="emails\translations\account-check_pt.txt" />
+    <Content Include="emails\translations\account-check_ru.html" />
+    <Content Include="emails\translations\account-check_ru.txt" />
+    <Content Include="emails\translations\account-check_tr.html" />
+    <Content Include="emails\translations\account-check_tr.txt" />
+    <Content Include="emails\translations\account-check_zh-chs.html" />
+    <Content Include="emails\translations\account-check_zh-chs.txt" />
+    <Content Include="emails\translations\account-check_zh-cht.html" />
+    <Content Include="emails\translations\account-check_zh-cht.txt" />
+    <Content Include="emails\translations\account-invite_cs.html" />
+    <Content Include="emails\translations\account-invite_cs.txt" />
+    <Content Include="emails\translations\account-invite_de.html" />
+    <Content Include="emails\translations\account-invite_de.txt" />
+    <Content Include="emails\translations\account-invite_es.html" />
+    <Content Include="emails\translations\account-invite_es.txt" />
+    <Content Include="emails\translations\account-invite_fi.html" />
+    <Content Include="emails\translations\account-invite_fi.txt" />
+    <Content Include="emails\translations\account-invite_fr.html" />
+    <Content Include="emails\translations\account-invite_fr.txt" />
+    <Content Include="emails\translations\account-invite_hi.html" />
+    <Content Include="emails\translations\account-invite_hi.txt" />
+    <Content Include="emails\translations\account-invite_ja.html" />
+    <Content Include="emails\translations\account-invite_ja.txt" />
+    <Content Include="emails\translations\account-invite_ko.html" />
+    <Content Include="emails\translations\account-invite_ko.txt" />
+    <Content Include="emails\translations\account-invite_nl.html" />
+    <Content Include="emails\translations\account-invite_nl.txt" />
+    <Content Include="emails\translations\account-invite_pt.html" />
+    <Content Include="emails\translations\account-invite_pt.txt" />
+    <Content Include="emails\translations\account-invite_ru.html" />
+    <Content Include="emails\translations\account-invite_ru.txt" />
+    <Content Include="emails\translations\account-invite_tr.html" />
+    <Content Include="emails\translations\account-invite_tr.txt" />
+    <Content Include="emails\translations\account-invite_zh-chs.html" />
+    <Content Include="emails\translations\account-invite_zh-chs.txt" />
+    <Content Include="emails\translations\account-invite_zh-cht.html" />
+    <Content Include="emails\translations\account-invite_zh-cht.txt" />
+    <Content Include="emails\translations\account-login_cs.html" />
+    <Content Include="emails\translations\account-login_cs.txt" />
+    <Content Include="emails\translations\account-login_de.html" />
+    <Content Include="emails\translations\account-login_de.txt" />
+    <Content Include="emails\translations\account-login_es.html" />
+    <Content Include="emails\translations\account-login_es.txt" />
+    <Content Include="emails\translations\account-login_fi.html" />
+    <Content Include="emails\translations\account-login_fi.txt" />
+    <Content Include="emails\translations\account-login_fr.html" />
+    <Content Include="emails\translations\account-login_fr.txt" />
+    <Content Include="emails\translations\account-login_hi.html" />
+    <Content Include="emails\translations\account-login_hi.txt" />
+    <Content Include="emails\translations\account-login_ja.html" />
+    <Content Include="emails\translations\account-login_ja.txt" />
+    <Content Include="emails\translations\account-login_ko.html" />
+    <Content Include="emails\translations\account-login_ko.txt" />
+    <Content Include="emails\translations\account-login_nl.html" />
+    <Content Include="emails\translations\account-login_nl.txt" />
+    <Content Include="emails\translations\account-login_pt.html" />
+    <Content Include="emails\translations\account-login_pt.txt" />
+    <Content Include="emails\translations\account-login_ru.html" />
+    <Content Include="emails\translations\account-login_ru.txt" />
+    <Content Include="emails\translations\account-login_tr.html" />
+    <Content Include="emails\translations\account-login_tr.txt" />
+    <Content Include="emails\translations\account-login_zh-chs.html" />
+    <Content Include="emails\translations\account-login_zh-chs.txt" />
+    <Content Include="emails\translations\account-login_zh-cht.html" />
+    <Content Include="emails\translations\account-login_zh-cht.txt" />
+    <Content Include="emails\translations\account-reset_cs.html" />
+    <Content Include="emails\translations\account-reset_cs.txt" />
+    <Content Include="emails\translations\account-reset_de.html" />
+    <Content Include="emails\translations\account-reset_de.txt" />
+    <Content Include="emails\translations\account-reset_es.html" />
+    <Content Include="emails\translations\account-reset_es.txt" />
+    <Content Include="emails\translations\account-reset_fi.html" />
+    <Content Include="emails\translations\account-reset_fi.txt" />
+    <Content Include="emails\translations\account-reset_fr.html" />
+    <Content Include="emails\translations\account-reset_fr.txt" />
+    <Content Include="emails\translations\account-reset_hi.html" />
+    <Content Include="emails\translations\account-reset_hi.txt" />
+    <Content Include="emails\translations\account-reset_ja.html" />
+    <Content Include="emails\translations\account-reset_ja.txt" />
+    <Content Include="emails\translations\account-reset_ko.html" />
+    <Content Include="emails\translations\account-reset_ko.txt" />
+    <Content Include="emails\translations\account-reset_nl.html" />
+    <Content Include="emails\translations\account-reset_nl.txt" />
+    <Content Include="emails\translations\account-reset_pt.html" />
+    <Content Include="emails\translations\account-reset_pt.txt" />
+    <Content Include="emails\translations\account-reset_ru.html" />
+    <Content Include="emails\translations\account-reset_ru.txt" />
+    <Content Include="emails\translations\account-reset_tr.html" />
+    <Content Include="emails\translations\account-reset_tr.txt" />
+    <Content Include="emails\translations\account-reset_zh-chs.html" />
+    <Content Include="emails\translations\account-reset_zh-chs.txt" />
+    <Content Include="emails\translations\account-reset_zh-cht.html" />
+    <Content Include="emails\translations\account-reset_zh-cht.txt" />
+    <Content Include="emails\translations\mesh-invite_cs.html" />
+    <Content Include="emails\translations\mesh-invite_cs.txt" />
+    <Content Include="emails\translations\mesh-invite_de.html" />
+    <Content Include="emails\translations\mesh-invite_de.txt" />
+    <Content Include="emails\translations\mesh-invite_es.html" />
+    <Content Include="emails\translations\mesh-invite_es.txt" />
+    <Content Include="emails\translations\mesh-invite_fi.html" />
+    <Content Include="emails\translations\mesh-invite_fi.txt" />
+    <Content Include="emails\translations\mesh-invite_fr.html" />
+    <Content Include="emails\translations\mesh-invite_fr.txt" />
+    <Content Include="emails\translations\mesh-invite_hi.html" />
+    <Content Include="emails\translations\mesh-invite_hi.txt" />
+    <Content Include="emails\translations\mesh-invite_ja.html" />
+    <Content Include="emails\translations\mesh-invite_ja.txt" />
+    <Content Include="emails\translations\mesh-invite_ko.html" />
+    <Content Include="emails\translations\mesh-invite_ko.txt" />
+    <Content Include="emails\translations\mesh-invite_nl.html" />
+    <Content Include="emails\translations\mesh-invite_nl.txt" />
+    <Content Include="emails\translations\mesh-invite_pt.html" />
+    <Content Include="emails\translations\mesh-invite_pt.txt" />
+    <Content Include="emails\translations\mesh-invite_ru.html" />
+    <Content Include="emails\translations\mesh-invite_ru.txt" />
+    <Content Include="emails\translations\mesh-invite_tr.html" />
+    <Content Include="emails\translations\mesh-invite_tr.txt" />
+    <Content Include="emails\translations\mesh-invite_zh-chs.html" />
+    <Content Include="emails\translations\mesh-invite_zh-chs.txt" />
+    <Content Include="emails\translations\mesh-invite_zh-cht.html" />
+    <Content Include="emails\translations\mesh-invite_zh-cht.txt" />
+    <Content Include="emails\translations\sms-messages_cs.txt" />
+    <Content Include="emails\translations\sms-messages_de.txt" />
+    <Content Include="emails\translations\sms-messages_es.txt" />
+    <Content Include="emails\translations\sms-messages_fi.txt" />
+    <Content Include="emails\translations\sms-messages_fr.txt" />
+    <Content Include="emails\translations\sms-messages_hi.txt" />
+    <Content Include="emails\translations\sms-messages_ja.txt" />
+    <Content Include="emails\translations\sms-messages_ko.txt" />
+    <Content Include="emails\translations\sms-messages_nl.txt" />
+    <Content Include="emails\translations\sms-messages_pt.txt" />
+    <Content Include="emails\translations\sms-messages_ru.txt" />
+    <Content Include="emails\translations\sms-messages_tr.txt" />
+    <Content Include="emails\translations\sms-messages_zh-chs.txt" />
+    <Content Include="emails\translations\sms-messages_zh-cht.txt" />
+    <Content Include="LICENSE" />
+    <Content Include="meshcentral-config-schema.json" />
+    <Content Include="package.json" />
+    <Content Include="public\clickonce\minirouter\Application Files\MeshMiniRouter_1_0_0_70\MeshMiniRouter.application" />
+    <Content Include="public\clickonce\minirouter\Application Files\MeshMiniRouter_1_0_0_70\MeshMiniRouter.exe.config.deploy" />
+    <Content Include="public\clickonce\minirouter\Application Files\MeshMiniRouter_1_0_0_70\MeshMiniRouter.exe.deploy" />
+    <Content Include="public\clickonce\minirouter\Application Files\MeshMiniRouter_1_0_0_70\MeshMiniRouter.exe.manifest" />
+    <Content Include="public\clickonce\minirouter\Application Files\MeshMiniRouter_1_0_0_70\MeshMiniRouter.ico.deploy" />
+    <Content Include="public\clickonce\minirouter\MeshMiniRouter.application" />
+    <Content Include="public\clickonce\minirouter\publish.htm" />
+    <Content Include="public\commander.htm" />
+    <Content Include="public\compress.wcc" />
+    <Content Include="public\favicon.ico" />
+    <Content Include="public\images-isdu\ComputerIcon.png" />
+    <Content Include="public\images-isdu\ComputerIcon2.png" />
+    <Content Include="public\images-isdu\IntelLogo.png" />
+    <Content Include="public\images-isdu\IntelLogoWhite42.png" />
+    <Content Include="public\images-isdu\ISDUTitle.png" />
+    <Content Include="public\images-isdu\TabConfig.png" />
+    <Content Include="public\images-isdu\TabConnection.png" />
+    <Content Include="public\images-isdu\TabDesktop.png" />
+    <Content Include="public\images-isdu\TabDiscovery.png" />
+    <Content Include="public\images-isdu\TabEvents.png" />
+    <Content Include="public\images-isdu\TabNetwork.png" />
+    <Content Include="public\images-isdu\TabPolicies.png" />
+    <Content Include="public\images-isdu\TabRemote.png" />
+    <Content Include="public\images-isdu\caution.gif" />
+    <Content Include="public\images-isdu\info.gif" />
+    <Content Include="public\images-isdu\warning.gif" />
+    <Content Include="public\images-isdu\isdu.ico" />
+    <Content Include="public\images\dot.png" />
+    <Content Include="public\images\icon-addnew.png" />
+    <Content Include="public\images\icon-graph.png" />
+    <Content Include="public\images\icon-installmesh.png" />
+    <Content Include="public\images\icon-manage.png" />
+    <Content Include="public\images\icon-remove.png" />
+    <Content Include="public\images\icons16.png" />
+    <Content Include="public\images\icons200-1-1.png" />
+    <Content Include="public\images\icons200-2-1.png" />
+    <Content Include="public\images\icons200-3-1.png" />
+    <Content Include="public\images\icons200-4-1.png" />
+    <Content Include="public\images\icons200-5-1.png" />
+    <Content Include="public\images\icons200-6-1.png" />
+    <Content Include="public\images\icons50.png" />
+    <Content Include="public\images\images16.png" />
+    <Content Include="public\images\info.png" />
+    <Content Include="public\images\link1.png" />
+    <Content Include="public\images\link2.png" />
+    <Content Include="public\images\link3.png" />
+    <Content Include="public\images\link4.png" />
+    <Content Include="public\images\logoback.png" />
+    <Content Include="public\images\mainaccount.png" />
+    <Content Include="public\images\mainwelcome.png" />
+    <Content Include="public\images\mapmarker-gps.png" />
+    <Content Include="public\images\mapmarker-ip.png" />
+    <Content Include="public\images\mapmarker-user.png" />
+    <Content Include="public\images\mapmarker-wifi.png" />
+    <Content Include="public\images\mapmarker.png" />
+    <Content Include="public\images\meshicon50.png" />
+    <Content Include="public\images\trash.png" />
+    <Content Include="public\mstsc\css\signin.css" />
+    <Content Include="public\mstsc\LICENSE" />
+    <Content Include="public\novnc\app\images\alt.svg" />
+    <Content Include="public\novnc\app\images\clipboard.svg" />
+    <Content Include="public\novnc\app\images\connect.svg" />
+    <Content Include="public\novnc\app\images\ctrl.svg" />
+    <Content Include="public\novnc\app\images\ctrlaltdel.svg" />
+    <Content Include="public\novnc\app\images\disconnect.svg" />
+    <Content Include="public\novnc\app\images\drag.svg" />
+    <Content Include="public\novnc\app\images\error.svg" />
+    <Content Include="public\novnc\app\images\esc.svg" />
+    <Content Include="public\novnc\app\images\expander.svg" />
+    <Content Include="public\novnc\app\images\fullscreen.svg" />
+    <Content Include="public\novnc\app\images\handle.svg" />
+    <Content Include="public\novnc\app\images\handle_bg.svg" />
+    <Content Include="public\novnc\app\images\info.svg" />
+    <Content Include="public\novnc\app\images\keyboard.svg" />
+    <Content Include="public\novnc\app\images\mouse_left.svg" />
+    <Content Include="public\novnc\app\images\mouse_middle.svg" />
+    <Content Include="public\novnc\app\images\mouse_none.svg" />
+    <Content Include="public\novnc\app\images\mouse_right.svg" />
+    <Content Include="public\novnc\app\images\power.svg" />
+    <Content Include="public\novnc\app\images\settings.svg" />
+    <Content Include="public\novnc\app\images\tab.svg" />
+    <Content Include="public\novnc\app\images\toggleextrakeys.svg" />
+    <Content Include="public\novnc\app\images\warning.svg" />
+    <Content Include="public\novnc\app\images\windows.svg" />
+    <Content Include="public\novnc\app\locale\cs.json" />
+    <Content Include="public\novnc\app\locale\de.json" />
+    <Content Include="public\novnc\app\locale\el.json" />
+    <Content Include="public\novnc\app\locale\es.json" />
+    <Content Include="public\novnc\app\locale\ko.json" />
+    <Content Include="public\novnc\app\locale\nl.json" />
+    <Content Include="public\novnc\app\locale\pl.json" />
+    <Content Include="public\novnc\app\locale\ru.json" />
+    <Content Include="public\novnc\app\locale\sv.json" />
+    <Content Include="public\novnc\app\locale\tr.json" />
+    <Content Include="public\novnc\app\locale\zh_CN.json" />
+    <Content Include="public\novnc\app\locale\zh_TW.json" />
+    <Content Include="public\novnc\app\sounds\bell.mp3" />
+    <Content Include="public\novnc\app\sounds\bell.oga" />
+    <Content Include="public\novnc\app\sounds\CREDITS" />
+    <Content Include="public\novnc\app\styles\base.css" />
+    <Content Include="public\novnc\app\styles\Orbitron700.ttf" />
+    <Content Include="public\novnc\app\styles\Orbitron700.woff" />
+    <Content Include="public\novnc\LICENSE.txt" />
+    <Content Include="public\novnc\vendor\browser-es-module-loader\dist\browser-es-module-loader.js.map" />
+    <Content Include="public\novnc\vendor\browser-es-module-loader\README.md" />
+    <Content Include="public\novnc\vendor\pako\LICENSE" />
+    <Content Include="public\novnc\vendor\pako\README.md" />
+    <Content Include="public\novnc\vnc.html" />
+    <Content Include="public\scriptblocks.txt" />
+    <Content Include="public\sounds\chimes.mp3" />
+    <Content Include="public\styles\font-awesome\css\font-awesome.min.css" />
+    <Content Include="public\styles\font-awesome\fonts\fontawesome-webfont.eot" />
+    <Content Include="public\styles\font-awesome\fonts\fontawesome-webfont.svg" />
+    <Content Include="public\styles\font-awesome\fonts\fontawesome-webfont.ttf" />
+    <Content Include="public\styles\font-awesome\fonts\fontawesome-webfont.woff" />
+    <Content Include="public\styles\font-awesome\fonts\fontawesome-webfont.woff2" />
+    <Content Include="public\styles\font-awesome\fonts\FontAwesome.otf" />
+    <Content Include="public\styles\font-awesome\less\animated.less" />
+    <Content Include="public\styles\font-awesome\less\bordered-pulled.less" />
+    <Content Include="public\styles\font-awesome\less\core.less" />
+    <Content Include="public\styles\font-awesome\less\fixed-width.less" />
+    <Content Include="public\styles\font-awesome\less\font-awesome.less" />
+    <Content Include="public\styles\font-awesome\less\icons.less" />
+    <Content Include="public\styles\font-awesome\less\larger.less" />
+    <Content Include="public\styles\font-awesome\less\list.less" />
+    <Content Include="public\styles\font-awesome\less\mixins.less" />
+    <Content Include="public\styles\font-awesome\less\path.less" />
+    <Content Include="public\styles\font-awesome\less\rotated-flipped.less" />
+    <Content Include="public\styles\font-awesome\less\screen-reader.less" />
+    <Content Include="public\styles\font-awesome\less\stacked.less" />
+    <Content Include="public\styles\font-awesome\less\variables.less" />
+    <Content Include="public\styles\font-awesome\scss\font-awesome.scss" />
+    <Content Include="public\styles\font-awesome\scss\_animated.scss" />
+    <Content Include="public\styles\font-awesome\scss\_bordered-pulled.scss" />
+    <Content Include="public\styles\font-awesome\scss\_core.scss" />
+    <Content Include="public\styles\font-awesome\scss\_fixed-width.scss" />
+    <Content Include="public\styles\font-awesome\scss\_icons.scss" />
+    <Content Include="public\styles\font-awesome\scss\_larger.scss" />
+    <Content Include="public\styles\font-awesome\scss\_list.scss" />
+    <Content Include="public\styles\font-awesome\scss\_mixins.scss" />
+    <Content Include="public\styles\font-awesome\scss\_path.scss" />
+    <Content Include="public\styles\font-awesome\scss\_rotated-flipped.scss" />
+    <Content Include="public\styles\font-awesome\scss\_screen-reader.scss" />
+    <Content Include="public\styles\font-awesome\scss\_stacked.scss" />
+    <Content Include="public\styles\font-awesome\scss\_variables.scss" />
+    <Content Include="public\styles\messenger.css" />
+    <Content Include="public\styles\style.css" />
+    <Content Include="public\translate.bat" />
+    <Content Include="public\translator.htm" />
+    <Content Include="rdp\LICENSE" />
+    <Content Include="rdp\README.md" />
+    <Content Include="readme.md" />
+    <Content Include="sample-config-advanced.json" />
+    <Content Include="sample-config.json" />
+    <Content Include="SECURITY.md" />
+    <Content Include="SourceFileList.txt" />
+    <Content Include="translate\readme.txt" />
+    <Content Include="translate\translate.json" />
+    <Content Include="views\agentinvite.handlebars" />
+    <Content Include="views\default-mobile.handlebars" />
+    <Content Include="views\default.handlebars" />
+    <Content Include="views\default3.handlebars" />
+    <Content Include="views\download.handlebars" />
+    <Content Include="views\download2.handlebars" />
+    <Content Include="views\error404-mobile.handlebars" />
+    <Content Include="views\error404.handlebars" />
+    <Content Include="views\error4042.handlebars" />
+    <Content Include="views\invite.handlebars" />
+    <Content Include="views\login-mobile.handlebars" />
+    <Content Include="views\login.handlebars" />
+    <Content Include="views\login2.handlebars" />
+    <Content Include="views\message.handlebars" />
+    <Content Include="views\message2.handlebars" />
+    <Content Include="views\messenger.handlebars" />
+    <Content Include="views\mstsc.handlebars" />
+    <Content Include="views\player.handlebars" />
+    <Content Include="views\sharing-mobile.handlebars" />
+    <Content Include="views\sharing.handlebars" />
+    <Content Include="views\ssh.handlebars" />
+    <Content Include="views\terms-mobile.handlebars" />
+    <Content Include="views\terms.handlebars" />
+    <Content Include="views\xterm.handlebars" />
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="agents\" />
+    <Folder Include="agents\modules_meshcmd\" />
+    <Folder Include="agents\modules_meshcmd_min\" />
+    <Folder Include="agents\modules_meshcore\" />
+    <Folder Include="agents\modules_meshcore_min\" />
+    <Folder Include="amt\" />
+    <Folder Include="emails\" />
+    <Folder Include="emails\translations\" />
+    <Folder Include="public" />
+    <Folder Include="public\clickonce\" />
+    <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\images-isdu" />
+    <Folder Include="public\images\" />
+    <Folder Include="public\mstsc\" />
+    <Folder Include="public\mstsc\css\" />
+    <Folder Include="public\mstsc\js\" />
+    <Folder Include="public\novnc\" />
+    <Folder Include="public\novnc\app\" />
+    <Folder Include="public\novnc\app\images\" />
+    <Folder Include="public\novnc\app\locale\" />
+    <Folder Include="public\novnc\app\sounds\" />
+    <Folder Include="public\novnc\app\styles\" />
+    <Folder Include="public\novnc\core\" />
+    <Folder Include="public\novnc\core\decoders\" />
+    <Folder Include="public\novnc\core\input\" />
+    <Folder Include="public\novnc\core\util\" />
+    <Folder Include="public\novnc\vendor\" />
+    <Folder Include="public\novnc\vendor\browser-es-module-loader\" />
+    <Folder Include="public\novnc\vendor\browser-es-module-loader\dist\" />
+    <Folder Include="public\novnc\vendor\browser-es-module-loader\src\" />
+    <Folder Include="public\novnc\vendor\pako\" />
+    <Folder Include="public\novnc\vendor\pako\lib\" />
+    <Folder Include="public\novnc\vendor\pako\lib\utils\" />
+    <Folder Include="public\novnc\vendor\pako\lib\zlib\" />
+    <Folder Include="public\scripts\" />
+    <Folder Include="public\sounds\" />
+    <Folder Include="public\styles\" />
+    <Folder Include="public\styles\font-awesome\" />
+    <Folder Include="public\styles\font-awesome\css\" />
+    <Folder Include="public\styles\font-awesome\fonts\" />
+    <Folder Include="public\styles\font-awesome\less\" />
+    <Folder Include="public\styles\font-awesome\scss\" />
+    <Folder Include="rdp\" />
+    <Folder Include="rdp\asn1\" />
+    <Folder Include="rdp\core\" />
+    <Folder Include="rdp\protocol\" />
+    <Folder Include="rdp\protocol\pdu\" />
+    <Folder Include="rdp\protocol\t125\" />
+    <Folder Include="rdp\security\" />
+    <Folder Include="translate\" />
+    <Folder Include="typings\" />
+    <Folder Include="typings\globals\" />
+    <Folder Include="typings\globals\ajv\" />
+    <Folder Include="typings\globals\async\" />
+    <Folder Include="typings\globals\axios\" />
+    <Folder Include="typings\globals\big-integer\" />
+    <Folder Include="typings\globals\busboy\" />
+    <Folder Include="typings\globals\connect-redis\" />
+    <Folder Include="typings\globals\cookie-session\" />
+    <Folder Include="typings\globals\core-js\" />
+    <Folder Include="typings\globals\dateformat\" />
+    <Folder Include="typings\globals\decimal.js\" />
+    <Folder Include="typings\globals\eventemitter2\" />
+    <Folder Include="typings\globals\express-handlebars\" />
+    <Folder Include="typings\globals\express-session\" />
+    <Folder Include="typings\globals\grunt\" />
+    <Folder Include="typings\globals\handlebars\" />
+    <Folder Include="typings\globals\he\" />
+    <Folder Include="typings\globals\hooker\" />
+    <Folder Include="typings\globals\http-errors\" />
+    <Folder Include="typings\globals\ip\" />
+    <Folder Include="typings\globals\is-plain-object\" />
+    <Folder Include="typings\globals\jsbn\" />
+    <Folder Include="typings\globals\klaw\" />
+    <Folder Include="typings\globals\load-json-file\" />
+    <Folder Include="typings\globals\localforage\" />
+    <Folder Include="typings\globals\lru-cache\" />
+    <Folder Include="typings\globals\marked\" />
+    <Folder Include="typings\globals\moment-timezone\" />
+    <Folder Include="typings\globals\moment\" />
+    <Folder Include="typings\globals\node-forge\" />
+    <Folder Include="typings\globals\nodemailer\" />
+    <Folder Include="typings\globals\node\" />
+    <Folder Include="typings\globals\object-assign\" />
+    <Folder Include="typings\globals\once\" />
+    <Folder Include="typings\globals\passport\" />
+    <Folder Include="typings\globals\pg-pool\" />
+    <Folder Include="typings\globals\rx-lite\" />
+    <Folder Include="typings\globals\split2\" />
+    <Folder Include="typings\globals\sprintf-js\" />
+    <Folder Include="typings\globals\sqlite3\" />
+    <Folder Include="typings\globals\type-check\" />
+    <Folder Include="typings\globals\ua-parser-js\" />
+    <Folder Include="typings\globals\underscore\" />
+    <Folder Include="typings\globals\uuid\" />
+    <Folder Include="typings\globals\window-size\" />
+    <Folder Include="views\" />
+  </ItemGroup>
+  <ItemGroup>
+    <TypeScriptCompile Include="typings\globals\ajv\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\async\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\axios\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\big-integer\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\busboy\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\connect-redis\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\cookie-session\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\core-js\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\dateformat\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\decimal.js\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\eventemitter2\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\grunt\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\handlebars\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\he\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\hooker\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\http-errors\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\ip\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\is-plain-object\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\jsbn\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\klaw\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\load-json-file\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\localforage\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\lru-cache\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\marked\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\moment-timezone\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\moment\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\node-forge\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\nodemailer\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\node\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\object-assign\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\once\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\passport\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\pg-pool\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\rx-lite\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\split2\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\sprintf-js\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\sqlite3\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\type-check\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\ua-parser-js\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\underscore\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\uuid\index.d.ts" />
+    <TypeScriptCompile Include="typings\globals\window-size\index.d.ts" />
+    <TypeScriptCompile Include="typings\index.d.ts" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <!--Do not delete the following Import Project.  While this appears to do nothing it is a marker for setting TypeScript properties before our import that depends on them.-->
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" Condition="False" />
+  <Import Project="$(VSToolsPath)\Node.js Tools\Microsoft.NodejsTools.targets" />
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
+        <WebProjectProperties>
+          <UseIIS>False</UseIIS>
+          <AutoAssignPort>True</AutoAssignPort>
+          <DevelopmentServerPort>0</DevelopmentServerPort>
+          <DevelopmentServerVPath>/</DevelopmentServerVPath>
+          <IISUrl>http://localhost:48022/</IISUrl>
+          <NTLMAuthentication>False</NTLMAuthentication>
+          <UseCustomServer>True</UseCustomServer>
+          <CustomServerUrl>http://localhost:1337</CustomServerUrl>
+          <SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
+        </WebProjectProperties>
+      </FlavorProperties>
+      <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}" User="">
+        <WebProjectProperties>
+          <StartPageUrl>
+          </StartPageUrl>
+          <StartAction>CurrentPage</StartAction>
+          <AspNetDebugging>True</AspNetDebugging>
+          <SilverlightDebugging>False</SilverlightDebugging>
+          <NativeDebugging>False</NativeDebugging>
+          <SQLDebugging>False</SQLDebugging>
+          <ExternalProgram>
+          </ExternalProgram>
+          <StartExternalURL>
+          </StartExternalURL>
+          <StartCmdLineArguments>
+          </StartCmdLineArguments>
+          <StartWorkingDirectory>
+          </StartWorkingDirectory>
+          <EnableENC>False</EnableENC>
+          <AlwaysStartWebServerOnDebug>False</AlwaysStartWebServerOnDebug>
+        </WebProjectProperties>
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>

+ 105 - 0
SECURITY.md

@@ -0,0 +1,105 @@
+# Security Policy
+
+## Supported Versions
+
+Any version of MeshCentral 1.x.x is supported.
+
+| Version | Supported          |
+| ------- | ------------------ |
+| 1.x.x   | :white_check_mark: |
+| < 1.0   | :x:                |
+
+## Reporting a Vulnerability
+
+Please report any concerns or security issue to Ylian Saint-Hilaire ([email protected]) and Simon Smith ([email protected]).
+
+Ylian Saint-Hilaire ([email protected]) PGP key below.
+```
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.56
+
+mQMuBF2gC4sRCAClFNvMCCVW3ego3UHBQ6LhSenJfaZYhvn8gaGuemSQxqTI6bla
+BTAv3aMtQnvqlSuadMMegb+FO6hnaQMlGvpVA1qpkSzgrPS5HrBD3H33J2Nj3i93
+ZpDPpxdI0ehCj6IJPnl0GxGbpKIN8YpJUFl44wv1lMRFI1lgyb+dCoO60irYdNQB
+PV85BI+DwPfOBFHunwR78nqMvpvsk9HaeHjEP7oXr952/7EazUowZsMlEfkYnw5S
++tLfpCoY3QWkektpJP40nMJSKQdV2NEuED99doA0X+7P1vsvFFFyMH69dnU2uSay
+XCHpkAbntBy0BGmtF1RnTcOMv2V/LPXnlMdvAQCbmLQzNra3r163tcdRY0jSs+pZ
+1L3w5tHNj2dzhfpa7wf/SIuds6QTr2LCN6miLoSVCRMMpT7d771b16GwQqWEXzN2
++h7dYqrssHPOa8FSUrPerz0+0eFcbMSm5/L/4KXWXoQthURv8aMP9E0iVoUYaaKB
+7U+5vFEZbpoOZyZmTAjXQMSNZCft0azA82Q+G85euyicWtMv48yNVzUhkdh+M2ud
+ohkXX2Aor1TqpBJoIeWke7j9D+Bo+lu61zPRx5ed9teUeLJCwqNEjlE+6gre5kxF
+PoreAtn59QYcBIpzQEWVMbNFlDAR4jMyqIoKCGfBPiRw2V+kunbzqiGQEglIFfOt
+6sTN/+CJh0ei976VDmE0Z1kMN+CNLgIjIw8fl02V9QgAnHcpqtVUxR4dbGOhVDq5
+lWv+K75QQlWyXC2k+KboXcaCvH0WZEBACYzO0CfrZ5hP9BSkbj5usSUVGGHwEFAJ
+t+/04KVY71fW281Ej5kGNaIKxeKsx6+hMo+UXb5ZM+6fANNNxs1cK95sTH6PjkyB
+tsKxLoa3CV2v9mSE5JiKKt74R9nXVo7PXf6DizwAU2l30Lb6y6y0OdXdCCPAG8Ij
+FrMgPu5MtjgsO5DnkZfUqDPWHhOgEPyOh3Ho+pvDhNYh5cm2eLQ8g5orzs2FHwbZ
+DpAHwCdqrlcpBlKJ4W/MZdf1fg2PjqaTWm7ZFiGr91P0F6kltTLWbVKTjLdS0T+D
+L7QnWWxpYW4gU2FpbnQtSGlsYWlyZSA8eWxpYW5zdEBnbWFpbC5jb20+iF4EExEI
+AAYFAl2gC4sACgkQg7j/r4DH+kD/3gD+MRedlM53VzOtNOpS6mqDAxj1aWP90HN0
+AqO6zuCTyGgBAJlunLFKH8IUetmQOhiohB8HVhdm/q4lKRDV7sHdplDyuMwEXaAL
+ixACAJSU/sCV87he4oZUKzg2/IGl3QoDSbTCOd04dE1IjPjjHbi8t9M7Qau55aM8
+ypFEsc7zMslL8Fc78EejrKmM3zsB/RU9XWFyrbQwRbaK6OHeEHC2E3AFaG0p09c6
+d0kZloHuWyEsm5a/3PpbIM1eP9IESJXWCc+bQQt6DxLKHLmkKMwB/icWMg8uMJlx
+aady8TEq7LH5oFVKsglnwuN1nIkecrf77TVkEqTjIxS6TiOup6zOnioFNKLYBAH0
+WUnJEYFvx4OIXgQYEQgABgUCXaALiwAKCRCDuP+vgMf6QGFTAQCUj2gGwsFlN0eR
+Wowv4eLcc3FwQ+lBElUctKg8vNFb0gD/ZWVWsWwKerNgNnf7RGD9mt8G2CKvdgGG
+oZ2hPP2gU9w=
+=roW4
+-----END PGP PUBLIC KEY BLOCK-----
+```
+Simon Smith ([email protected]) PGP key below.
+```
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBGgUVVwBEADVReB3GclVt2B9928Cs2XVXc0BW/AcKP4XOdrWHE0u9dTtozrM
+EI2azZI+tcyGDVGdNJlYF2FQUTnnVcazm+IUskZJ0mgL+TQ2k+bRmQXj1ZRKrTk5
+5qOjPCxP0jCHbxHIlh9ecO3NsHHj1+MFRDzGBvOyiJzVxn2xmg8+r7okcnRZj7Wj
+0K4EwM33/Xdanl8IXahwaQH3rQrn5gjunLB2vFvrC6sxpU8Q9etEwUc7/D8zfH9f
+9K11hESeQvMVkfv/QGrO0X5nhDEYiH4aJA7SqvC6xhIqjyNfAqRoMpUDGwP/JB6B
+3WHyhUfkNDT9gfn064BF90NZQgr318Z0uzuwC4NHvptVz5pFJYpUz9KBkkn8p+Mq
+j82IwHeMXyvO4nzBxfmqDYfN4vrnbVJmcMNtQUe2G9ekKHTO7UHW1gH4ls68tzNA
+COexEBfTKpPqboK1yM8wwycvyXuX8DEVZtbiKXNcUPSOdmUx1ZXtO6YHfSDiRWgu
+HxCN+LoWujbI22ry9YZcOHkG6c45vmavw/7ebB7XVl4yQeneZVxxH3nFSccPrBFg
+NYROw/j0yeeNu2CATsutJSblQqQuROYdUhkJACnpF6mE60eUs+Slk1C0OQZfUm/z
+kpJP5dXRm+AhzoprmSLl0umMdcyBhZHTk27pkt/yVFNxAxMwMOsLj1NeXQARAQAB
+tCZTaW1vbiBTbWl0aCA8c2ltb25zbWl0aDU1MjFAZ21haWwuY29tPokCVAQTAQgA
+PhYhBEwpnGdhitVYgbKU3iDv9oNlI537BQJoFFVcAhsDBQkHhh9kBQsJCAcCBhUK
+CQgLAgQWAgMBAh4BAheAAAoJECDv9oNlI537fqYP/1l27lv+/jEnhPfMYmy0TXAV
+RQES7afzNRH9bbVW4RMrVito5PtUJ71KAP0UznYHUuo2hVZeuBRDqgVBreeb/KHZ
+ERz8UgTJhE/FBhBGKKgHGtPWwCuQs2owGCePQHweFUVh5O5NWjXPBrZ6s1GipDwC
+Gi6UG9pgKcs7iGc2hIwV/EF06QgKmKhyL1twpmu4wzjdRoja0Xp3GFjgqcQG82bP
+OGxA4znyzXoNQAWPUr4PW+cGDSGPar3BUnQhHCssQTYbjQHeaW7VH9pvv217a/Yh
+sUgzp0KMhyp8ShBrhJJJdddWxaPuDhip5ABK2lCRqK1utCpBCZ2CjmAd5RtkVuwF
+DLMDa5siiO844n8Hc1MQ+P1k6iNi8JlPcwSLiKn9jeP+/UfFQbCzx3Fhx+6NVOGQ
+k9JqhyQPvEl4F1RUqjlhFmebFiXt4PnhZct3MO1CfNCsaYUkulCbzzoQV54qHDvY
+z2GQ5G3mV6CExWuVAnPq/GqOnyVP/4bmnWF/wkYZDzMfSws9kNgMoRD7UZ5PQS1U
+lpg8HCiyMR64ZeFZxb6tjc0jbuxafYe402QEUIfyLt2iHW5Jb1ksld+ncE5WYOIq
+jtC+MN1txhJgmgXWvvmSH+R42Q9PwEvtzKDksZGqNhlu/R5kHVrMN9OW8q5b2r6q
+04Q95ICfI8OSUUT7J3o3uQINBGgUVVwBEADcTCtuskpBj002JvF02gvLUB0tAzQJ
+sv4PtPZlUvV8BbzfpvPEW5xB2MjUbyhFqJTJj9wDr+ELx+hE1X/GzgfGNDvXSCnq
+0E8KEWS++CmYAL0HdZx9nJiXet7FGE90FTuZMWrS1Rojs2vMZESN/hqBP8kuWK9x
+z+Bq/xiRHeFM5zUXTUfVghZSUOv/mpCU9tN/h//yI8Ltnke3Xk4m6OSDfQKinmZY
+ynhZFrWmhwcAm7DzNZIJ8P5fvOmJlrQQbsGYUqLUNNFSgmW/+bNnOyB5+oBLV3ou
+6OhMDhoEdwLaGr9/W+Wxc5hYyM455rgO3NIVg/js244shJrz58xo3Ral0P7xZt2O
+hSyNtFOVZboFxRV4ESCSVS3oB4K/aC76WHzhTcxhWkRmM6Q8nWhqS2A5dhZeWWK6
+RmHMqqirSwd0IeFQq8rpUSPaXoU6cfoT5Zv/VXr3hP+Tdd/m8rMqMF/5tqerGP79
+ofqGE89ydulP+dr+jJNPaiJ8Alx7hszuB1PTFMZMvlHMWPhx0xXjTjGaJt4y551y
+8XyvEeAZ9u58UHhT9gquumu4sh55Rw0PMp23BUsNBHSq+0VMQBoZlxJa3w7QC7i/
+TM1NRtsAJAoM0ocyca7EztDLoSsCi9j8nedzhiH3dB0U+ma5dtRWrVSSc1NV09po
+Og5aJdav2YW33QARAQABiQI8BBgBCAAmFiEETCmcZ2GK1ViBspTeIO/2g2UjnfsF
+AmgUVVwCGwwFCQeGH2QACgkQIO/2g2UjnfuCaA//Z+Ur69OnOxIqY0makkJFnQO6
+I4657VNuE9dMWtQwQA7nn/Rxxqk0hAuEPF6uyX4sZ20SK5LUQQyhyz3fwYiNm9Rr
+iYkTmPiMgx7CPsRVXBPjRWow+z5Li/RYPMlYNwKJ2h8z/+w2VHaA/WkkfLQKeWQq
+T5Hr7+5WQzOOHcntESBI5mZw/4CPEaWJEIWWrD7TtsWiYfRnOHlB/GvDQcaXxKVa
+Gp1rsmOh5t/prdG/F4jLSsYvpXLY618t3Zn1SJYhWub6h8OtF3Mrao7Wm/HSwcwM
+0IWAnDYB1K70MvPLEifqcyeYZFZE8gFuOU/WpyHHUSpVvGJ6fhL6sKWmCRJGR4UJ
+3kepyYspshMVZRwJkll8UdrKHcAsUnOML25JX712BDNNHsJTcMf1i9BhIwlxk2yN
+BspmWe7JJHD28FCoQ3tpvf+iN5uILO/QrtYkiigppfpP56ZFdTVtk/LdnTfh5gPl
+FIOJKHXZKEM9zbhW/ntnXDKQjnWw/3EuFvmmqOqEtHF+pRDZiRjzptJIOdg392h8
+GM8EnU927FMbtnpysQu9sxUGxMDZ9GMEOnFpAtg2LV4bnHx+K6g3JL07BdB3gdmX
+SieMJS9Az3lcIqzMqtmyti7S2eP+0aduXOmxE1QtPuzs5X7a0XXSvBAoI79Az10V
+z1Ncl3xSEOPFKUIvLck=
+=yUbV
+-----END PGP PUBLIC KEY BLOCK-----
+```

+ 13 - 0
SourceFileList.txt

@@ -0,0 +1,13 @@
+readme.txt
+license.txt
+package.json
+sample-config.json
+*.js
+views/*
+public/*
+public/images/*
+public/scripts/*
+public/styles/*
+agents/MeshAgent-Win32.exe
+MeshCentral.sln
+MeshCentralServer.njsproj

BIN
agents/MeshAgentOSXPackager.zip


BIN
agents/MeshCentralAssistant.exe


BIN
agents/MeshCentralRouter.dmg


BIN
agents/MeshCentralRouter.exe


BIN
agents/MeshCentralRouterCmd.exe


BIN
agents/MeshCentralSatellite.exe


BIN
agents/MeshCmd.exe


BIN
agents/MeshCmd64.exe


BIN
agents/MeshCmdARM64.exe


BIN
agents/MeshCommander-Small.gz


BIN
agents/MeshService.exe


BIN
agents/MeshService64.exe


BIN
agents/MeshServiceARM64.exe


+ 771 - 0
agents/agent-translations.json

@@ -0,0 +1,771 @@
+{
+  "en": {
+    "agent": "Agent",
+    "agentVersion": "New Version",
+    "group": "Device Group",
+    "url": "Server URL",
+    "meshName": "Group Name",
+    "meshId": "Group Identifier",
+    "serverId": "Server Identifier",
+    "setup": "Setup",
+    "update": "Update",
+    "install": "Install",
+    "uninstall": "Uninstall",
+    "connect": "Connect",
+    "disconnect": "Disconnect",
+    "cancel": "Cancel",
+    "close": "Close",
+    "pressok": "Press OK to disconnect",
+    "elevation": "Elevated permissions are required to install/uninstall this software.",
+    "sudo": "Please try again with sudo.",
+    "ctrlc": "Press Ctrl-C to exit.",
+    "commands": "You can run the text version from the command line with the following command(s)",
+    "graphicalerror": "The graphical version of this installer cannot run on this system",
+    "zenity": "Try installing/updating Zenity, and run again",
+    "status": [
+      "NOT INSTALLED",
+      "RUNNING",
+      "NOT RUNNING"
+    ],
+    "statusDescription": "Current Agent Status",
+    "description": "Click the buttons below to install or uninstall this remote management software. When installed, this software runs in the background allowing this computer to be managed and controlled by a remote administrator.",
+    "connectionDetailsButton": "Connection Details..."
+  },
+  "cs": {
+    "agent": "Agent",
+    "group": "Skupina zařízení",
+    "url": "URL serveru",
+    "meshName": "Skupinové jméno",
+    "meshId": "Identifikátor skupiny",
+    "serverId": "Identifikátor serveru",
+    "setup": "Nastavit",
+    "update": "Aktualizace",
+    "install": "Instalace",
+    "uninstall": "Odinstalace",
+    "connect": "Připojit",
+    "disconnect": "Odpojit",
+    "cancel": "Storno",
+    "close": "Zavřít",
+    "pressok": "Stisknutím tlačítka OK se odpojíte",
+    "sudo": "Zkuste to prosím znovu s sudo.",
+    "ctrlc": "Ukončete stisknutím Ctrl-C.",
+    "commands": "Textovou verzi můžete spustit z příkazového řádku pomocí následujících příkazů",
+    "zenity": "Zkuste nainstalovat / aktualizovat Zenity a spustit znovu",
+    "status": [
+      "NENÍ INSTALOVÁN",
+      "SPUŠTĚNO",
+      "NEFUNGUJE"
+    ],
+    "statusDescription": "Aktuální stav agenta",
+    "agentVersion": "Nová verze",
+    "elevation": "K instalaci/odinstalaci tohoto softwaru jsou vyžadována zvýšená oprávnění.",
+    "graphicalerror": "Na tomto systému nelze spustit grafickou verzi tohoto instalačního programu",
+    "description": "Klepnutím na tlačítka níže nainstalujete nebo odinstalujete tento software pro vzdálenou správu. Když je tento software nainstalován, běží na pozadí, což umožňuje spravovat a ovládat tento počítač vzdáleným správcem.",
+    "connectionDetailsButton": "Podrobnosti o připojení..."
+  },
+  "de": {
+    "agent": "Agent",
+    "group": "Gerätegruppe",
+    "url": "Server-URL",
+    "meshName": "Gruppenname",
+    "meshId": "Gruppen-ID",
+    "serverId": "Server-ID",
+    "setup": "Konfiguration",
+    "update": "Aktualisierung",
+    "install": "Installieren",
+    "uninstall": "Deinstallation",
+    "connect": "Verbinden",
+    "disconnect": "Trennen",
+    "cancel": "Abbrechen",
+    "close": "Schließen",
+    "pressok": "Drücken Sie OK, um die Verbindung zu trennen",
+    "sudo": "Bitte versuchen Sie es erneut mit sudo.",
+    "ctrlc": "Drücken Sie Strg-C, um den Vorgang zu beenden.",
+    "commands": "Sie können die Textversion über die Befehlszeile mit den folgenden Befehlen ausführen.",
+    "zenity": "Versuchen Sie, Zenity zu installieren / zu aktualisieren, und führen Sie es erneut aus",
+    "status": [
+      "NICHT INSTALLIERT",
+      "GESTARTET",
+      "NICHT GESTARTET"
+    ],
+    "statusDescription": "Aktueller Agentstatus",
+    "agentVersion": "Neue Version",
+    "elevation": "Zum Installieren/Deinstallieren dieser Software sind erhöhte Berechtigungen erforderlich.",
+    "graphicalerror": "Die grafische Version dieses Installationsprogramms kann auf diesem System nicht ausgeführt werden",
+    "description": "Klicken Sie auf die Schaltflächen unten, um diese Fernverwaltungssoftware zu installieren oder zu deinstallieren. Nach der Installation wird diese Software im Hintergrund ausgeführt, sodass dieser Computer von einem entfernten Administrator verwaltet und gesteuert werden kann.",
+    "connectionDetailsButton": "Verbindungsinformationen...",
+    "connectionDetailsTitle": "Verbindungsinformationen"
+  },
+  "es": {
+    "agent": "Agente",
+    "group": "Grupo de Dispositivos",
+    "url": "URL del servidor",
+    "meshName": "Nombre del grupo",
+    "meshId": "Identificador del Grupo",
+    "serverId": "Identificador de Servidor",
+    "setup": "Preparar",
+    "update": "Actualizar",
+    "install": "Instalar",
+    "uninstall": "Desinstalar",
+    "connect": "Conectar",
+    "disconnect": "Desconectar",
+    "cancel": "Cancelar",
+    "close": "Cerrar",
+    "pressok": "Presione OK para desconectar",
+    "sudo": "Vuelve a intentarlo con sudo.",
+    "ctrlc": "Presione Ctrl-C para salir.",
+    "commands": "Puedes ejecutar la versión de texto desde la línea de comandos con los siguientes comandos",
+    "zenity": "Intenta instalar / actualizar Zenity y vuelve a ejecutar",
+    "status": [
+      "NO INSTALADO",
+      "EJECUTANDO",
+      "NO EJECUTANDOSE"
+    ],
+    "statusDescription": "Estado Actual del Agente",
+    "agentVersion": "Nueva versión",
+    "elevation": "Se requieren permisos elevados para instalar/desinstalar este software.",
+    "graphicalerror": "La versión gráfica de este instalador no puede ejecutarse en este sistema",
+    "description": "Haga clic en los botones a continuación para instalar o desinstalar este software de administración remota. Cuando se instala, este software se ejecuta en segundo plano, lo que permite que un administrador remoto administre y controle esta computadora.",
+    "connectionDetailsButton": "Detalles de conexión..."
+  },
+  "fi": {
+    "agent": "Agentti",
+    "group": "Laiteryhmä",
+    "url": "Palvelimen URL-osoite",
+    "meshName": "Ryhmän nimi",
+    "meshId": "Ryhmän tunniste",
+    "serverId": "Palvelimen tunniste",
+    "setup": "Perustaa",
+    "update": "Päivittää",
+    "install": "Asenna",
+    "uninstall": "Asennuksen poistaminen",
+    "connect": "Yhdistä",
+    "disconnect": "Katkaise yhteys",
+    "cancel": "Peruuta",
+    "close": "Sulje",
+    "pressok": "Katkaise yhteys painamalla OK",
+    "sudo": "Yritä uudelleen sudolla.",
+    "ctrlc": "Poistu painamalla Ctrl-C.",
+    "commands": "Voit suorittaa tekstiversio komentoriviltä seuraavilla komennoilla",
+    "zenity": "Yritä asentaa / päivittää Zenity ja suorita uudelleen",
+    "status": [
+      "EI ASENNETTU",
+      "Juoksu",
+      "EI RUNNING"
+    ],
+    "statusDescription": "Agentin nykyinen tila",
+    "agentVersion": "Uusi versio",
+    "elevation": "Tämän ohjelmiston asentaminen/asennuksen poistaminen edellyttää korotettuja käyttöoikeuksia.",
+    "graphicalerror": "Tämän asennusohjelman graafista versiota ei voi käyttää tässä järjestelmässä",
+    "description": "Napsauta alla olevia painikkeita asentaaksesi tai poistaaksesi tämän etähallintaohjelmiston. Kun ohjelmisto on asennettu, se toimii taustalla, jolloin etäjärjestelmänvalvoja voi hallita ja ohjata tätä tietokonetta.",
+    "connectionDetailsButton": "Yhteyden tiedot..."
+  },
+  "fr": {
+    "agent": "Agent",
+    "group": "Groupe d'appareils",
+    "url": "URL du serveur",
+    "meshName": "Nom du groupe",
+    "meshId": "Identifiant de groupe",
+    "serverId": "Identifiant du serveur",
+    "setup": "Configuration",
+    "update": "Mettre à jour",
+    "install": "Installer",
+    "uninstall": "Désinstaller",
+    "connect": "Se connecter",
+    "disconnect": "Déconnecter",
+    "cancel": "Annuler",
+    "close": "Fermer",
+    "pressok": "Appuyez sur OK pour vous déconnecter",
+    "sudo": "Veuillez réessayer avec sudo.",
+    "ctrlc": "Appuyez sur Ctrl-C pour quitter.",
+    "commands": "Vous pouvez exécuter la version texte à partir de la ligne de commande avec la ou les commandes suivantes",
+    "zenity": "Essayez d'installer / mettre à jour Zenity et réexécutez",
+    "status": [
+      "PAS INSTALLÉ",
+      "FONCTIONNEMENT",
+      "PAS EN FONCTIONNEMENT"
+    ],
+    "statusDescription": "Statut actuel de l'agent",
+    "agentVersion": "Nouvelle version",
+    "elevation": "Des autorisations élevées sont requises pour installer/désinstaller ce logiciel.",
+    "graphicalerror": "La version graphique de ce programme d'installation ne peut pas s'exécuter sur ce système",
+    "description": "Cliquez sur les boutons ci-dessous pour installer ou désinstaller ce logiciel de gestion à distance. Une fois installé, ce logiciel s'exécute en arrière-plan, ce qui permet à cet ordinateur d'être géré et contrôlé par un administrateur distant.",
+    "connectionDetailsButton": "Détails de la connexion..."
+  },
+  "hi": {
+    "agent": "एजेंट",
+    "group": "डिवाइस समूह",
+    "url": "सर्वर URL",
+    "meshName": "समूह नाम",
+    "meshId": "समूह पहचानकर्ता",
+    "serverId": "सर्वर पहचानकर्ता",
+    "setup": "सेट अप",
+    "update": "अपडेट करें",
+    "install": "इंस्टॉल",
+    "uninstall": "स्थापना रद्द करें",
+    "connect": "जुडिये",
+    "disconnect": "डिस्कनेक्ट",
+    "cancel": "रद्द करना",
+    "close": "बंद करे",
+    "pressok": "डिस्कनेक्ट करने के लिए ओके दबाएं",
+    "sudo": "कृपया सूडो के साथ फिर से प्रयास करें।",
+    "ctrlc": "बाहर निकलने के लिए Ctrl-C दबाएं।",
+    "commands": "आप निम्न कमांड के साथ कमांड लाइन से टेक्स्ट संस्करण चला सकते हैं",
+    "zenity": "ज़ेनिटी को स्थापित / अपडेट करने का प्रयास करें, और फिर से चलाएं",
+    "status": [
+      "स्थापित नहीं है",
+      "चल रहा है",
+      "चल नहीं रहा"
+    ],
+    "statusDescription": "वर्तमान एजेंट की स्थिति",
+    "agentVersion": "नया संस्करण",
+    "elevation": "इस सॉफ़्टवेयर को स्थापित/अनइंस्टॉल करने के लिए उन्नत अनुमतियों की आवश्यकता होती है।",
+    "graphicalerror": "इस इंस्टालर का आलेखीय संस्करण इस सिस्टम पर नहीं चल सकता",
+    "description": "इस दूरस्थ प्रबंधन सॉफ़्टवेयर को स्थापित या अनइंस्टॉल करने के लिए नीचे दिए गए बटनों पर क्लिक करें। स्थापित होने पर, यह सॉफ़्टवेयर पृष्ठभूमि में चलता है जिससे इस कंप्यूटर को दूरस्थ व्यवस्थापक द्वारा प्रबंधित और नियंत्रित किया जा सकता है।",
+    "connectionDetailsButton": "कनेक्शन विवरण..."
+  },
+  "it": {
+    "agent": "Agente",
+    "group": "Gruppo di dispositivi",
+    "url": "URL del server",
+    "meshName": "Nome del gruppo",
+    "meshId": "Identificatore di gruppo",
+    "serverId": "Identificatore del server",
+    "setup": "Impostare",
+    "update": "Aggiornamenti",
+    "install": "Installare",
+    "uninstall": "Disinstalla",
+    "connect": "Connetti",
+    "disconnect": "Disconnetti",
+    "cancel": "Annulla",
+    "close": "Chiudere",
+    "pressok": "Premi OK per disconnetterti",
+    "sudo": "Riprova con sudo.",
+    "ctrlc": "Premi Ctrl-C per uscire.",
+    "commands": "Puoi eseguire la versione testuale dalla riga di comando con i seguenti comandi ",
+    "zenity": "Prova a installare/aggiornare Zenity ed esegui di nuovo",
+    "status": [
+      "NON INSTALLATO",
+      "IN ESECUZIONE",
+      "NON IN ESECUZIONE"
+    ],
+    "statusDescription": "Stato attuale dell'agente",
+    "agentVersion": "Nuova versione",
+    "elevation": "Per installare/disinstallare questo software sono necessarie autorizzazioni elevate.",
+    "graphicalerror": "La versione grafica di questo programma di installazione non può essere eseguita su questo sistema",
+    "description": "Fare clic sui pulsanti seguenti per installare o disinstallare questo software di gestione remota. Una volta installato, questo software viene eseguito in background consentendo a questo computer di essere gestito e controllato da un amministratore remoto.",
+    "connectionDetailsButton": "Dettagli di connessione..."
+  },
+  "ja": {
+    "agent": "エージェント",
+    "group": "デバイスグループ",
+    "url": "サーバーのURL",
+    "meshName": "그룹 이름",
+    "meshId": "グループ識別子",
+    "serverId": "サーバー識別子",
+    "setup": "セットアップ",
+    "update": "更新",
+    "install": "インストール",
+    "uninstall": "アンインストール",
+    "connect": "つなぐ",
+    "disconnect": "切断する",
+    "cancel": "キャンセル",
+    "close": "閉じる",
+    "pressok": "OKを押して切断します",
+    "sudo": "sudoでもう一度やり直してください。",
+    "ctrlc": "Ctrl-Cを押して終了します。",
+    "commands": "次のコマンドを使用して、コマンドラインからテキストバージョンを実行できます。",
+    "zenity": "Zenityをインストール/更新して、もう一度実行してください",
+    "status": [
+      "インストールされていない",
+      "ランニング",
+      "走っていない"
+    ],
+    "statusDescription": "現在のエージェントステータス",
+    "agentVersion": "新しいバージョン",
+    "elevation": "このソフトウェアをインストール/アンインストールするには、昇格された権限が必要です。",
+    "graphicalerror": "このインストーラーのグラフィカルバージョンは、このシステムでは実行できません",
+    "description": "このリモート管理ソフトウェアをインストールまたはアンインストールするには、下のボタンをクリックしてください。インストールすると、このソフトウェアはバックグラウンドで実行され、リモート管理者がこのコンピューターを管理および制御できるようになります。",
+    "connectionDetailsButton": "接続の詳細..."
+  },
+  "ko": {
+    "agent": "에이전트",
+    "group": "장치 그룹",
+    "url": "서버의 위치",
+    "meshName": "그룹 이름",
+    "meshId": "그룹 식별자",
+    "serverId": "서버의 식별자",
+    "setup": "설정하다",
+    "update": "개조하다",
+    "install": "설치",
+    "uninstall": "설치 제거",
+    "connect": "연결",
+    "disconnect": "연결 해제",
+    "cancel": "취소",
+    "close": "닫기",
+    "pressok": "연결을 끊으려면 \"OK\"를 누르십시오",
+    "sudo": "\"sudo\"로 다시 시도하십시오",
+    "ctrlc": "종료하려면 \"Ctrl-C\"를 누르십시오.",
+    "commands": "다음 명령을 사용하여 콘솔에서 텍스트 버전을 실행할 수 있습니다",
+    "zenity": "\"Zenity\"를 설치 또는 업데이트하고 다시 시도하십시오",
+    "status": [
+      "없다",
+      "운영",
+      "중지됨"
+    ],
+    "statusDescription": "에이전트 상태",
+    "agentVersion": "새로운 버전",
+    "elevation": "이 소프트웨어를 설치/제거하려면 높은 권한이 필요합니다.",
+    "graphicalerror": "이 설치 프로그램의 그래픽 버전은 이 시스템에서 실행할 수 없습니다.",
+    "description": "이 원격 관리 소프트웨어를 설치하거나 제거하려면 아래 버튼을 클릭하십시오. 이 소프트웨어를 설치하면 백그라운드에서 실행되어 원격 관리자가 이 컴퓨터를 관리하고 제어할 수 있습니다.",
+    "connectionDetailsButton": "연결 세부정보..."
+  },
+  "nl": {
+    "agent": "Agent",
+    "group": "Apparaat groep",
+    "url": "Server URL",
+    "meshName": "Groepsnaam",
+    "meshId": "Groepsidentificatie",
+    "serverId": "Server identificatie",
+    "setup": "Setup",
+    "update": "Bijwerken",
+    "install": "Installeren",
+    "uninstall": "Deïnstallatie",
+    "connect": "Verbinden",
+    "disconnect": "Verbreken",
+    "cancel": "Annuleren",
+    "close": "Sluiten",
+    "pressok": "Druk op OK om de verbinding te verbreken",
+    "sudo": "Probeer het opnieuw met sudo.",
+    "ctrlc": "Druk op Ctrl-C om af te sluiten.",
+    "commands": "U kunt de tekstversie vanaf de opdrachtregel uitvoeren met de volgende opdracht(en)",
+    "zenity": "Probeer Zenity te installeren / bij te werken en voer het opnieuw uit",
+    "status": [
+      "NIET GEÏNSTALLEERD",
+      "ACTIEF",
+      "NIET ACTIEF"
+    ],
+    "statusDescription": "Huidige agent status",
+    "agentVersion": "Nieuwe versie",
+    "elevation": "Verhoogde machtigingen zijn vereist om deze software te installeren/verwijderen.",
+    "graphicalerror": "De grafische versie van dit installatieprogramma kan niet op dit systeem draaien",
+    "description": "Klik op de onderstaande knoppen om deze software voor beheer op afstand te installeren of te verwijderen. Na installatie draait deze software op de achtergrond waardoor deze computer kan worden beheerd en gecontroleerd door een externe beheerder.",
+    "connectionDetailsButton": "Verbindingsdetails..."
+  },
+  "pt": {
+    "agent": "Agente",
+    "group": "Grupo de dispositivos",
+    "url": "URL do servidor",
+    "meshName": "Nome do grupo",
+    "meshId": "Identificador de Grupo",
+    "serverId": "Identificador de Servidor",
+    "setup": "Configuração",
+    "update": "Atualizar",
+    "install": "Instalar",
+    "uninstall": "Desinstalar",
+    "connect": "Conectar",
+    "disconnect": "Desconectar",
+    "cancel": "Cancelar",
+    "close": "Fechar",
+    "pressok": "Pressione OK para desconectar",
+    "sudo": "Por favor, tente novamente com sudo.",
+    "ctrlc": "Pressione Ctrl-C para sair.",
+    "commands": "Você pode executar a versão em texto a partir da linha de comando com o (s) seguinte (s) comando (s)",
+    "zenity": "Tente instalar / atualizar o Zenity e execute novamente",
+    "status": [
+      "NÃO INSTALADO",
+      "CORRIDA",
+      "NÃO CORRENDO"
+    ],
+    "statusDescription": "Status atual do agente",
+    "agentVersion": "Nova versão",
+    "elevation": "Permissões elevadas são necessárias para instalar/desinstalar este software.",
+    "graphicalerror": "A versão gráfica deste instalador não pode ser executada neste sistema",
+    "description": "Clique nos botões abaixo para instalar ou desinstalar este software de gerenciamento remoto. Quando instalado, este software é executado em segundo plano, permitindo que este computador seja gerenciado e controlado por um administrador remoto.",
+    "connectionDetailsButton": "Detalhes da conexão..."
+  },
+  "ru": {
+    "agent": "Агент",
+    "group": "Группа устройств",
+    "url": "URL-адрес сервера",
+    "meshName": "Имя Группы",
+    "meshId": "Идентификатор группы",
+    "serverId": "Идентификатор сервера",
+    "setup": "Настроить",
+    "update": "Обновить",
+    "install": "Установка",
+    "uninstall": "Удаление",
+    "connect": "Подключиться",
+    "disconnect": "Разъединить",
+    "cancel": "Отмена",
+    "close": "Закрыть",
+    "pressok": "Нажмите ОК для отключения",
+    "sudo": "Пожалуйста, попробуйте еще раз с помощью sudo.",
+    "ctrlc": "Нажмите Ctrl-C для выхода.",
+    "commands": "Вы можете запустить текстовую версию из командной строки с помощью следующих команд",
+    "zenity": "Попробуйте установить / обновить Zenity и снова запустить",
+    "status": [
+      "НЕ УСТАНОВЛЕНО",
+      "ЗАПУЩЕН",
+      "НЕ ЗАПУЩЕН"
+    ],
+    "statusDescription": "Текущий статус агента",
+    "agentVersion": "Новая версия",
+    "elevation": "Для установки/удаления этого программного обеспечения требуются повышенные права.",
+    "graphicalerror": "Графическая версия этого установщика не может быть запущена в этой системе.",
+    "description": "Нажмите кнопки ниже, чтобы установить или удалить это программное обеспечение для удаленного управления..После установки это программное обеспечение работает в фоновом режиме, что позволяет удаленному администратору управлять этим компьютером.",
+    "connectionDetailsButton": "Подробности подключения..."
+  },
+  "sv": {
+    "agent": "Agent",
+    "group": "Enhetsgrupp",
+    "url": "Serverns URL",
+    "meshName": "Grupp namn",
+    "meshId": "Gruppidentifierare",
+    "serverId": "Serveridentifierare",
+    "setup": "Uppstart",
+    "update": "Uppdatering",
+    "install": "Installera",
+    "uninstall": "Avinstallera",
+    "connect": "Anslut",
+    "disconnect": "Koppla ifrån",
+    "cancel": "Avbryt",
+    "close": "Stäng",
+    "pressok": "Tryck på OK för att koppla bort",
+    "sudo": "Försök igen med sudo.",
+    "ctrlc": "Tryck på Ctrl-C för att avsluta.",
+    "commands": "Du kan köra textversionen från kommandoraden med följande kommando (er)",
+    "zenity": "Försök att installera / uppdatera Zenity och kör igen",
+    "status": [
+      "INTE INSTALLERAD",
+      "LÖPNING",
+      "SPRINGER INTE"
+    ],
+    "statusDescription": "Aktuell agentstatus",
+    "agentVersion": "Ny version",
+    "elevation": "Förhöjda behörigheter krävs för att installera/avinstallera denna programvara.",
+    "graphicalerror": "Den grafiska versionen av detta installationsprogram kan inte köras på det här systemet",
+    "description": "Klicka på knapparna nedan för att installera eller avinstallera denna fjärrhanteringsprogramvara. När den är installerad körs den här programvaran i bakgrunden så att den här datorn kan hanteras och kontrolleras av en fjärradministratör.",
+    "connectionDetailsButton": "Anslutningsdetaljer..."
+  },
+  "tr": {
+    "agent": "Agent",
+    "group": "Cihaz Grubu",
+    "url": "Sunucu URL'si",
+    "meshName": "Grup ismi",
+    "meshId": "Grup Tanımlayıcı",
+    "serverId": "Sunucu Tanımlayıcı",
+    "setup": "Kurmak",
+    "update": "Güncelleme",
+    "install": "Yüklemek",
+    "uninstall": "Kaldır",
+    "connect": "Bağlan",
+    "disconnect": "Bağlantıyı kes",
+    "cancel": "İptal",
+    "close": "Kapat",
+    "pressok": "Bağlantıyı kesmek için Tamam'a basın",
+    "sudo": "Lütfen sudo ile tekrar deneyin.",
+    "ctrlc": "Çıkmak için Ctrl-C tuşlarına basın.",
+    "commands": "Metin sürümünü aşağıdaki komutlarla komut satırından çalıştırabilirsiniz.",
+    "zenity": "Zenity'yi kurmayı / güncellemeyi deneyin ve tekrar çalıştırın",
+    "status": [
+      "YÜKLÜ DEĞİL",
+      "KOŞU",
+      "KOŞMAK YOK"
+    ],
+    "statusDescription": "Mevcut Agent Durumu",
+    "agentVersion": "Yeni sürüm",
+    "elevation": "Bu yazılımı yüklemek/kaldırmak için yüksek izinler gerekir.",
+    "graphicalerror": "Bu yükleyicinin grafik sürümü bu sistemde çalışamaz",
+    "description": "Bu uzaktan yönetim yazılımını yüklemek veya kaldırmak için aşağıdaki düğmelere tıklayın. Yüklendiğinde, bu yazılım arka planda çalışır ve bu bilgisayarın uzak bir yönetici tarafından yönetilmesine ve kontrol edilmesine olanak tanır.",
+    "connectionDetailsButton": "Bağlantı ayrıntıları..."
+  },
+  "zh-chs": {
+    "agent": "代理",
+    "group": "设备组",
+    "url": "服务器网址",
+    "meshName": "团队名字",
+    "meshId": "组标识符",
+    "serverId": "服务器标识符",
+    "setup": "设定",
+    "update": "更新",
+    "install": "安装",
+    "uninstall": "卸载",
+    "connect": "连接",
+    "disconnect": "断线",
+    "cancel": "取消",
+    "close": "关",
+    "pressok": "按确定断开连接",
+    "sudo": "请使用sudo再试一次。",
+    "ctrlc": "按Ctrl-C退出。",
+    "commands": "您可以使用以下命令从命令行运行文本版本",
+    "zenity": "尝试安装/更新Zenity,然后再次运行",
+    "status": [
+      "未安装",
+      "正在运行",
+      "不在运行"
+    ],
+    "statusDescription": "当前代理状态",
+    "agentVersion": "新版本",
+    "elevation": "安装/卸载此软件需要提升权限。",
+    "graphicalerror": "此安装程序的图形版本无法在此系统上运行",
+    "description": "单击下面的按钮以安装或卸载此远程管理软件。安装后,该软件在后台运行,允许远程管理员管理和控制该计算机。",
+    "connectionDetailsButton": "连接详情..."
+  },
+  "zh-cht": {
+    "agent": "代理",
+    "group": "裝置群",
+    "url": "服務器網址",
+    "meshName": "團隊名字",
+    "meshId": "群標識符",
+    "serverId": "服務器標識符",
+    "setup": "設定",
+    "update": "更新資料",
+    "install": "安裝",
+    "uninstall": "卸載",
+    "connect": "連接",
+    "disconnect": "斷線",
+    "cancel": "取消",
+    "close": "關",
+    "pressok": "按確定斷開連接",
+    "sudo": "請使用sudo再試一次。",
+    "ctrlc": "按Ctrl-C退出。",
+    "commands": "您可以使用以下命令從命令行運行文本版本",
+    "zenity": "嘗試安裝/更新Zenity,然後再次運行",
+    "status": [
+      "未安裝",
+      "正在運行",
+      "不在運行"
+    ],
+    "statusDescription": "當前代理狀態",
+    "agentVersion": "新版本",
+    "elevation": "安裝/卸載此軟件需要提升權限。",
+    "graphicalerror": "此安裝程序的圖形版本無法在此系統上運行",
+    "description": "單擊下面的按鈕以安裝或卸載此遠程管理軟件。安裝後,該軟件在後台運行,允許遠程管理員管理和控制該計算機。",
+    "connectionDetailsButton": "連接詳情..."
+  },
+  "da": {
+    "agent": "Agent",
+    "group": "Enhedsgruppe",
+    "url": "Server URL",
+    "meshName": "Gruppe navn",
+    "meshId": "Gruppe-id",
+    "serverId": "Serveridentifikator",
+    "setup": "Opsætning",
+    "update": "Opdatering",
+    "install": "Installér",
+    "uninstall": "Afinstaller",
+    "connect": "Forbind",
+    "disconnect": "Afbryd",
+    "cancel": "Annuller",
+    "close": "Luk",
+    "pressok": "Tryk på OK for at afbryde",
+    "sudo": "Prøv venligst igen med sudo.",
+    "ctrlc": "Tryk på Ctrl-C for at afslutte.",
+    "commands": "Du kan køre tekstversionen fra kommandolinjen med følgende kommando(er)",
+    "zenity": "Prøv at installere/opdatere Zenity, og kør igen",
+    "status": [
+      "IKKE INSTALLERET",
+      "KØRER",
+      "KØRER IKKE"
+    ],
+    "statusDescription": "Aktuel agentstatus",
+    "agentVersion": "Ny version",
+    "elevation": "Forhøjede tilladelser er nødvendige for at installere/afinstallere denne software.",
+    "graphicalerror": "Den grafiske version af dette installationsprogram kan ikke køre på dette system",
+    "description": "Klik på knapperne nedenfor for at installere eller afinstallere denne fjernstyringssoftware. Når den er installeret, kører denne software i baggrunden, så denne computer kan administreres og kontrolleres af en fjernadministrator.",
+    "connectionDetailsButton": "Forbindelsesdetaljer..."
+  },
+  "pl": {
+    "agent": "Agent",
+    "group": "Grupa Urządzeń",
+    "url": "URL Serwera",
+    "meshName": "Nazwa Grupy",
+    "meshId": "Identyfikator Grupy",
+    "serverId": "Identyfikator Serwera",
+    "setup": "Konfiguracja",
+    "update": "Aktualizacja",
+    "install": "Zainstaluj",
+    "uninstall": "Odinstaluj",
+    "connect": "Połącz",
+    "disconnect": "Rozłącz",
+    "cancel": "Anuluj",
+    "close": "Zamknij",
+    "pressok": "Naciśnij OK by się rozłączyć",
+    "sudo": "Proszę spróbować ponownie z sudo.",
+    "ctrlc": "Naciśnij Ctrl-C by wyjść.",
+    "commands": "Możesz uruchomić wersję tekstową z linii poleceń za pomocą następujących komend",
+    "zenity": "Spróbuj zainstalować/zaktualizować Zenity i uruchom ponownie",
+    "status": [
+      "NIE ZAINSTALOWANY",
+      "DZIAŁA",
+      "NIE DZIAŁA"
+    ],
+    "statusDescription": "Obecny Status Agenta",
+    "agentVersion": "Nowa Wersja",
+    "elevation": "Do zainstalowania/odinstalowania tego oprogramowania wymagane są podwyższone uprawnienia.",
+    "graphicalerror": "Graficzna wersja tego instalatora nie może być uruchomiona w tym systemie",
+    "description": "Kliknij poniższe przyciski, aby zainstalować lub odinstalować oprogramowanie zdalnego dostępu. Po zainstalowaniu, to oprogramowanie działa w tle, umożliwiając zarządzanie i kontrolowanie tego komputera przez zdalnego administratora.",
+    "connectionDetailsButton": "Szczegóły połączenia..."
+  },
+  "pt-br": {
+    "agent": "Agente",
+    "group": "Grupo de Dispositivos",
+    "url": "URL do servidor",
+    "meshName": "Nome do grupo",
+    "meshId": "Identificador de Grupo",
+    "serverId": "Identificador de Servidor",
+    "setup": "Configurar",
+    "update": "Atualizar",
+    "install": "Instalar",
+    "uninstall": "Desinstalar",
+    "connect": "Conectar",
+    "disconnect": "Desconectar",
+    "cancel": "Cancelar",
+    "close": "Fechar",
+    "pressok": "Pressione OK para desconectar",
+    "sudo": "Por favor, tente novamente com sudo.",
+    "ctrlc": "Pressione Ctrl-C para sair.",
+    "commands": "Você pode executar a versão em texto a partir da linha de comando com o (s) seguinte (s) comando (s)",
+    "zenity": "Tente instalar / atualizar o Zenity e execute novamente",
+    "status": [
+      "NÃO INSTALADO",
+      "Executando",
+      "NÃO EXECUTANDO"
+    ],
+    "statusDescription": "Status atual do agente",
+    "agentVersion": "Nova Versão",
+    "elevation": "Permissões Elevadas são necessárias para instalar/desinstalar este software",
+    "graphicalerror": "A versão gráfica do instalador não pode ser executada neste sistema",
+    "description": "Clique nos botões abaixo para instalar ou desinstalar  este software de gerenciamento remoto. Quando instalado, este software é executado em segundo plano permitindo que este computador seja gerenciado e controlado por um administrador remoto",
+    "connectionDetailsButton": "Detalhes da conexão..."
+  },
+  "bs": {
+    "agent": "Agent",
+    "agentVersion": "Nova verzija",
+    "group": "Grupa uređaja",
+    "url": "URL servera",
+    "meshName": "Ime grupe",
+    "meshId": "Grupni identifikator",
+    "serverId": "Identifikator servera",
+    "setup": "Postaviti",
+    "update": "Ažuriraj",
+    "install": "Instaliraj",
+    "uninstall": "Deinstalirati",
+    "connect": "Povežite se",
+    "disconnect": "Prekini vezu",
+    "cancel": "Otkaži",
+    "close": "Zatvori",
+    "pressok": "Pritisnite OK da prekinete vezu",
+    "elevation": "Za instaliranje/deinstaliranje ovog softvera potrebne su povišene dozvole.",
+    "sudo": "Molimo pokušajte ponovo sa sudo.",
+    "ctrlc": "Pritisnite Ctrl-C za izlaz.",
+    "commands": "Možete pokrenuti tekstualnu verziju iz komandne linije sa sljedećim naredbama",
+    "graphicalerror": "Grafička verzija ovog instalatera ne može da radi na ovom sistemu",
+    "zenity": "Pokušajte instalirati/ažurirati Zenity i pokrenite ponovo",
+    "status": [
+      "NIJE INSTALIRANO",
+      "RUNNING",
+      "NOT RUNNING"
+    ],
+    "statusDescription": "Trenutni status agenta",
+    "description": "Kliknite na dugmad ispod da instalirate ili deinstalirate ovaj softver za daljinsko upravljanje. Kada je instaliran, ovaj softver radi u pozadini, što omogućava da ovim računarom upravlja i kontroliše udaljeni administrator.",
+    "connectionDetailsButton": "Detalji veze..."
+  },
+  "hu": {
+    "agent": "Agent",
+    "agentVersion": "Új verzió",
+    "group": "Eszköz csoport",
+    "url": "Kiszolgáló URL",
+    "meshName": "Csoport név",
+    "meshId": "Csoport azonosító",
+    "serverId": "Kiszolgáló azonosító",
+    "setup": "Beállítás",
+    "update": "Frissítés",
+    "install": "Telepítés",
+    "uninstall": "Eltávolítás",
+    "connect": "Kapcsolódás",
+    "disconnect": "Lekapcsolódás",
+    "cancel": "Mégse",
+    "close": "Bezár",
+    "pressok": "Press OK to disconnect",
+    "elevation": "A szoftver telepítéséhez/eltávolításához megnövelt jogosultságok szükségesek.",
+    "sudo": "Kérjük, próbálja meg újra a sudo használatával.",
+    "ctrlc": "A kilépéshez nyomja meg a Ctrl-C billentyűt.",
+    "commands": "A szöveges változatot a parancssorból futtathatja a következő parancs(okk)al",
+    "graphicalerror": "A telepítő grafikus verziója nem futtatható ezen a rendszeren.",
+    "zenity": "Próbálja meg telepíteni/frissíteni a Zenity-t, és indítsa újra",
+    "status": [
+      "NINCS TELEPÍTVE",
+      "FUT",
+      "NEM FUT"
+    ],
+    "statusDescription": "Jelenlegi agent állapota",
+    "description": "Kattintson a Telepítés vagy Eltávolítás gombokra a Távfelügyeleti alkalmazás telepítéséhez vagy eltávolításához. Telepítés után ez az alkalmazás a háttérben fut, lehetővé téve, hogy a számítógépet egy távoli rendszergazda kezelje.",
+    "connectionDetailsButton": "Kapcsolat részletei..."
+  },
+  "ca": {
+    "agent": "Agent",
+    "agentVersion": "Nova versió",
+    "group": "Grup de dispositius",
+    "url": "URL del servidor",
+    "meshName": "Nom del grup",
+    "meshId": "Identificador de grup",
+    "serverId": "Identificador del servidor",
+    "setup": "Configuració",
+    "update": "Actualització",
+    "install": "Instal·lar",
+    "uninstall": "Desinstal·la",
+    "connect": "Connecta't",
+    "disconnect": "Desconnecta",
+    "cancel": "Cancel · lar",
+    "close": "Tanca",
+    "pressok": "Premeu D'acord per desconnectar",
+    "elevation": "Es necessiten permisos elevats per instal·lar/desinstal·lar aquest programari.",
+    "sudo": "Si us plau, torna-ho a provar amb sudo.",
+    "ctrlc": "Premeu Ctrl-C per sortir.",
+    "commands": "Podeu executar la versió de text des de la línia d'ordres amb les següents ordres",
+    "graphicalerror": "La versió gràfica d'aquest instal·lador no pot executar-se en aquest sistema",
+    "zenity": "Proveu d'instal·lar/actualitzar Zenity i torneu a executar-lo",
+    "status": [
+      "NO ESTÀ INSTAL · LAT",
+      "CÓRRER",
+      "NO CORRE"
+    ],
+    "statusDescription": "Estat actual de l'agent",
+    "description": "Feu clic als botons següents per instal·lar o desinstal·lar aquest programari de gestió remota. Quan s'instal·la, aquest programari s'executa en segon pla i permet que aquest ordinador sigui gestionat i controlat per un administrador remot.",
+    "connectionDetailsButton": "Detalls de la connexió..."
+  },
+  "uk": {
+    "agent": "Агент",
+    "agentVersion": "Нова версія",
+    "group": "Група пристроїв",
+    "url": "URL Сервера",
+    "meshName": "Ім'я групи",
+    "meshId": "Ідентифікатор групи",
+    "serverId": "Ідентифікатор сервера",
+    "setup": "Налаштувати",
+    "update": "Оновити",
+    "install": "Інсталювати",
+    "uninstall": "Видалити",
+    "connect": "Підключитись",
+    "disconnect": "Відключити",
+    "cancel": "Скасувати",
+    "close": "Закрити",
+    "pressok": "Натисніть OK, щоб від'єднатись",
+    "elevation": "Для встановлення/видалення цієї програми потрібні права адміністратора.",
+    "sudo": "Будь ласка, спробуйте ще раз за допомогою sudo.",
+    "ctrlc": "Натисніть Ctrl-C, щоб вийти",
+    "commands": "Ви можете запустити текстову версію з командного рядка за допомогою таких команд",
+    "graphicalerror": "Графічна версія цього інсталятора не може запуститись в цій системі",
+    "zenity": "Спробуйте встановити або оновити Zenity, а тоді запустіть програму знову",
+    "status": [
+      "НЕ ВСТАНОВЛЕНО",
+      "ПРАЦЮЄ",
+      "НЕ ПРАЦЮЄ"
+    ],
+    "statusDescription": "Поточний статус Агента",
+    "description": "Щоб встановити або видалити це програмне забезпечення для віддаленого керування, скористайтеся кнопками нижче. Після інсталяції програма працютиме у фоновому режимі, що дозволить віддаленому адміністратору керувати цим комп'ютером. Підключення ж надасть тимчасовий доступ поки це вікно відкрите",
+    "connectionDetailsButton": "Деталі з'єднання..."
+  }
+}

+ 471 - 0
agents/agentrecoverycore.js

@@ -0,0 +1,471 @@
+
+var http = require('http');
+var childProcess = require('child_process');
+var meshCoreObj = { "action": "coreinfo", "value": "MeshCore Recovery", "caps": 14 }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript
+var nextTunnelIndex = 1;
+var tunnels = {};
+var fs = require('fs');
+
+//attachDebugger({ webport: 9994, wait: 1 }).then(function (p) { console.log('Debug on port: ' + p); });
+
+function sendConsoleText(msg)
+{
+    require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": msg });
+}
+// Return p number of spaces 
+function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
+
+var path =
+    {
+        join: function ()
+        {
+            var x = [];
+            for (var i in arguments)
+            {
+                var w = arguments[i];
+                if (w != null)
+                {
+                    while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
+                    if (i != 0)
+                    {
+                        while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
+                    }
+                    x.push(w);
+                }
+            }
+            if (x.length == 0) return '/';
+            return x.join('/');
+        }
+    };
+// Convert an object to string with all functions
+function objToString(x, p, pad, ret) {
+    if (ret == undefined) ret = '';
+    if (p == undefined) p = 0;
+    if (x == null) { return '[null]'; }
+    if (p > 8) { return '[...]'; }
+    if (x == undefined) { return '[undefined]'; }
+    if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
+    if (typeof x == 'buffer') { return '[buffer]'; }
+    if (typeof x != 'object') { return x; }
+    var r = '{' + (ret ? '\r\n' : ' ');
+    for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
+    return r + addPad(p, pad) + '}';
+}
+
+// Split a string taking into account the quoats. Used for command line parsing
+function splitArgs(str)
+{
+    var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
+    do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
+    return myArray;
+}
+
+// Parse arguments string array into an object
+function parseArgs(argv)
+{
+    var results = { '_': [] }, current = null;
+    for (var i = 1, len = argv.length; i < len; i++) {
+        var x = argv[i];
+        if (x.length > 2 && x[0] == '-' && x[1] == '-') {
+            if (current != null) { results[current] = true; }
+            current = x.substring(2);
+        } else {
+            if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
+        }
+    }
+    if (current != null) { results[current] = true; }
+    return results;
+}
+// Get server target url with a custom path
+function getServerTargetUrl(path)
+{
+    var x = require('MeshAgent').ServerUrl;
+    //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
+    if (x == null) { return null; }
+    if (path == null) { path = ''; }
+    x = http.parseUri(x);
+    if (x == null) return null;
+    return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
+}
+
+// Get server url. If the url starts with "*/..." change it, it not use the url as is.
+function getServerTargetUrlEx(url)
+{
+    if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
+    return url;
+}
+
+require('MeshAgent').on('Connected', function ()
+{
+    require('os').name().then(function (v)
+    {
+        sendConsoleText("Mesh Agent Receovery Console, OS: " + v);
+        require('MeshAgent').SendCommand(meshCoreObj);
+    });
+});
+
+// Tunnel callback operations
+function onTunnelUpgrade(response, s, head) {
+    this.s = s;
+    s.httprequest = this;
+    s.end = onTunnelClosed;
+    s.tunnel = this;
+
+    //sendConsoleText('onTunnelUpgrade');
+
+    if (this.tcpport != null) {
+        // This is a TCP relay connection, pause now and try to connect to the target.
+        s.pause();
+        s.data = onTcpRelayServerTunnelData;
+        var connectionOptions = { port: parseInt(this.tcpport) };
+        if (this.tcpaddr != null) { connectionOptions.host = this.tcpaddr; } else { connectionOptions.host = '127.0.0.1'; }
+        s.tcprelay = net.createConnection(connectionOptions, onTcpRelayTargetTunnelConnect);
+        s.tcprelay.peerindex = this.index;
+    } else {
+        // This is a normal connect for KVM/Terminal/Files
+        s.data = onTunnelData;
+    }
+}
+
+require('MeshAgent').AddCommandHandler(function (data)
+{
+    if (typeof data == 'object')
+    {
+        // If this is a console command, parse it and call the console handler
+        switch (data.action)
+        {
+            case 'msg':
+                {
+                    switch (data.type)
+                    {
+                    case 'console': { // Process a console command
+                        if (data.value && data.sessionid)
+                        {
+                            var args = splitArgs(data.value);
+                            processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
+                        }
+                        break;
+                    }
+                        case 'tunnel':
+                            {
+                        if (data.value != null) { // Process a new tunnel connection request
+                            // Create a new tunnel object
+                            var xurl = getServerTargetUrlEx(data.value);
+                            if (xurl != null) {
+                                var woptions = http.parseUri(xurl);
+                                woptions.rejectUnauthorized = 0;
+                                //sendConsoleText(JSON.stringify(woptions));
+                                var tunnel = http.request(woptions);
+                                tunnel.on('upgrade', function (response, s, head)
+                                {
+                                    this.s = s;
+                                    s.httprequest = this;
+                                    s.tunnel = this;
+                                    s.on('end', function ()
+                                    {
+                                        if (tunnels[this.httprequest.index] == null) return; // Stop duplicate calls.
+
+                                        // If there is a upload or download active on this connection, close the file
+                                        if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; }
+                                        if (this.httprequest.downloadFile) { fs.closeSync(this.httprequest.downloadFile); this.httprequest.downloadFile = undefined; }
+
+
+                                        //sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
+                                        delete tunnels[this.httprequest.index];
+
+                                        // Clean up WebSocket
+                                        this.removeAllListeners('data');
+                                    });
+                                    s.on('data', function (data)
+                                    {
+                                        // If this is upload data, save it to file
+                                        if (this.httprequest.uploadFile)
+                                        {
+                                            try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
+                                            this.write(new Buffer(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data
+                                            return;
+                                        }
+
+                                        if (this.httprequest.state == 0) {
+                                            // Check if this is a relay connection
+                                            if (data == 'c') { this.httprequest.state = 1; sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid); }
+                                        } else {
+                                            // Handle tunnel data
+                                            if (this.httprequest.protocol == 0)
+                                            {
+                                                // Take a look at the protocol
+                                                this.httprequest.protocol = parseInt(data);
+                                                if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
+                                                if (this.httprequest.protocol == 1) 
+                                                {
+                                                    // Remote terminal using native pipes
+                                                    if (process.platform == "win32")
+                                                    {
+                                                        this.httprequest._term = require('win-terminal').Start(80, 25);
+                                                        this.httprequest._term.pipe(this, { dataTypeSkip: 1 });
+                                                        this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false });
+                                                        this.prependListener('end', function () { this.httprequest._term.end(function () { sendConsoleText('Terminal was closed'); }); });
+                                                    }
+                                                    else
+                                                    {
+                                                        this.httprequest.process = childProcess.execFile("/bin/sh", ["sh"], { type: childProcess.SpawnTypes.TERM });
+                                                        this.httprequest.process.tunnel = this;
+                                                        this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); });
+                                                        this.httprequest.process.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); });
+                                                        this.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
+                                                        this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
+                                                        this.prependListener('end', function () { this.httprequest.process.kill(); });
+                                                    }
+
+                                                    this.on('end', function () {
+                                                        if (process.platform == "win32")
+                                                        {
+                                                            // Unpipe the web socket
+                                                            this.unpipe(this.httprequest._term);
+                                                            this.httprequest._term.unpipe(this);
+
+                                                            // Clean up
+                                                            this.httprequest._term.end();
+                                                            this.httprequest._term = null;
+                                                        }
+                                                    });
+                                                }
+                                            }
+                                            else if (this.httprequest.protocol == 5)
+                                            {
+                                                // Process files commands
+                                                var cmd = null;
+                                                try { cmd = JSON.parse(data); } catch (e) { };
+                                                if (cmd == null) { return; }
+                                                if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { return; } // If this is control data, handle it now.
+                                                if (cmd.action == undefined) { return; }
+                                                console.log('action: ', cmd.action);
+
+                                                //sendConsoleText('CMD: ' + JSON.stringify(cmd));
+
+                                                if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
+                                                //console.log(objToString(cmd, 0, ' '));
+                                                switch (cmd.action)
+                                                {
+                                                    case 'ls':
+                                                        // Send the folder content to the browser
+                                                        var response = getDirectoryInfo(cmd.path);
+                                                        if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
+                                                        this.write(new Buffer(JSON.stringify(response)));
+                                                        break;
+                                                    case 'mkdir': {
+                                                        // Create a new empty folder
+                                                        fs.mkdirSync(cmd.path);
+                                                        break;
+                                                    }
+                                                    case 'mkfile': {
+                                                        // Create a new empty file
+                                                        fs.closeSync(fs.openSync(cmd.path, 'w'));
+                                                        break;
+                                                    }
+                                                    case 'rm': {
+                                                        // Delete, possibly recursive delete
+                                                        for (var i in cmd.delfiles)
+                                                        {
+                                                            try { deleteFolderRecursive(path.join(cmd.path, cmd.delfiles[i]), cmd.rec); } catch (e) { }
+                                                        }
+                                                        break;
+                                                    }
+                                                    case 'rename': {
+                                                        // Rename a file or folder
+                                                        var oldfullpath = path.join(cmd.path, cmd.oldname);
+                                                        var newfullpath = path.join(cmd.path, cmd.newname);
+                                                        try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
+                                                        break;
+                                                    }
+                                                    case 'upload': {
+                                                        // Upload a file, browser to agent
+                                                        if (this.httprequest.uploadFile != undefined) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; }
+                                                        if (cmd.path == undefined) break;
+                                                        var filepath = cmd.name ? path.join(cmd.path, cmd.name) : cmd.path;
+                                                        try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
+                                                        this.httprequest.uploadFileid = cmd.reqid;
+                                                        if (this.httprequest.uploadFile) { this.write(new Buffer(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
+                                                        break;
+                                                    }
+                                                    case 'copy': {
+                                                        // Copy a bunch of files from scpath to dspath
+                                                        for (var i in cmd.names) {
+                                                            var sc = path.join(cmd.scpath, cmd.names[i]), ds = path.join(cmd.dspath, cmd.names[i]);
+                                                            if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
+                                                        }
+                                                        break;
+                                                    }
+                                                    case 'move': {
+                                                        // Move a bunch of files from scpath to dspath
+                                                        for (var i in cmd.names) {
+                                                            var sc = path.join(cmd.scpath, cmd.names[i]), ds = path.join(cmd.dspath, cmd.names[i]);
+                                                            if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
+                                                        }
+                                                        break;
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    });
+                                });
+                                tunnel.onerror = function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); }
+                                tunnel.sessionid = data.sessionid;
+                                tunnel.rights = data.rights;
+                                tunnel.state = 0;
+                                tunnel.url = xurl;
+                                tunnel.protocol = 0;
+                                tunnel.tcpaddr = data.tcpaddr;
+                                tunnel.tcpport = data.tcpport;
+                                tunnel.end();
+                                // Put the tunnel in the tunnels list
+                                var index = nextTunnelIndex++;
+                                tunnel.index = index;
+                                tunnels[index] = tunnel;
+
+                                //sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
+                            }
+                        }
+                        break;
+                    }
+
+                    default:
+                        // Unknown action, ignore it.
+                        break;
+                }
+                break;
+            }
+            default:
+                // Unknown action, ignore it.
+                break;
+        }
+    }
+});
+
+function processConsoleCommand(cmd, args, rights, sessionid)
+{
+    try
+    {
+        var response = null;
+        switch (cmd)
+        {
+            case 'help':
+                response = 'Available commands are: osinfo, dbkeys, dbget, dbset, dbcompact, netinfo.';
+                break;
+            
+            case 'osinfo': { // Return the operating system information
+                var i = 1;
+                if (args['_'].length > 0) { i = parseInt(args['_'][0]); if (i > 8) { i = 8; } response = 'Calling ' + i + ' times.'; }
+                for (var j = 0; j < i; j++) {
+                    var pr = require('os').name();
+                    pr.sessionid = sessionid;
+                    pr.then(function (v) { sendConsoleText("OS: " + v, this.sessionid); });
+                }
+                break;
+            }
+            case 'dbkeys': { // Return all data store keys
+                response = JSON.stringify(db.Keys);
+                break;
+            }
+            case 'dbget': { // Return the data store value for a given key
+                if (db == null) { response = 'Database not accessible.'; break; }
+                if (args['_'].length != 1) {
+                    response = 'Proper usage: dbget (key)'; // Display the value for a given database key
+                } else {
+                    response = db.Get(args['_'][0]);
+                }
+                break;
+            }
+            case 'dbset': { // Set a data store key and value pair
+                if (db == null) { response = 'Database not accessible.'; break; }
+                if (args['_'].length != 2) {
+                    response = 'Proper usage: dbset (key) (value)'; // Set a database key
+                } else {
+                    var r = db.Put(args['_'][0], args['_'][1]);
+                    response = 'Key set: ' + r;
+                }
+                break;
+            }
+            case 'dbcompact': { // Compact the data store
+                if (db == null) { response = 'Database not accessible.'; break; }
+                var r = db.Compact();
+                response = 'Database compacted: ' + r;
+                break;
+            }
+            case 'tunnels': { // Show the list of current tunnels
+                response = '';
+                for (var i in tunnels) { response += 'Tunnel #' + i + ', ' + tunnels[i].url + '\r\n'; }
+                if (response == '') { response = 'No websocket sessions.'; }
+                break;
+            }
+            case 'netinfo': { // Show network interface information
+                //response = objToString(mesh.NetInfo, 0, ' ');
+                var interfaces = require('os').networkInterfaces();
+                response = objToString(interfaces, 0, ' ', true);
+                break;
+            }
+            default: { // This is an unknown command, return an error message
+                response = 'Unknown command \"' + cmd + '\", type \"help\" for list of available commands.';
+                break;
+            }
+        }
+    } catch (e) { response = 'Command returned an exception error: ' + e; console.log(e); }
+    if (response != null) { sendConsoleText(response, sessionid); }
+}
+
+// Get a formated response for a given directory path
+function getDirectoryInfo(reqpath)
+{
+    var response = { path: reqpath, dir: [] };
+    if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) {
+        // List all the drives in the root, or the root itself
+        var results = null;
+        try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
+        if (results != null) {
+            for (var i = 0; i < results.length; ++i) {
+                var drive = { n: results[i].name, t: 1 };
+                if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
+                response.dir.push(drive);
+            }
+        }
+    } else {
+        // List all the files and folders in this path
+        if (reqpath == '') { reqpath = '/'; }
+        var results = null, xpath = path.join(reqpath, '*');
+        //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
+        try { results = fs.readdirSync(xpath); } catch (e) { }
+        if (results != null) {
+            for (var i = 0; i < results.length; ++i) {
+                if ((results[i] != '.') && (results[i] != '..')) {
+                    var stat = null, p = path.join(reqpath, results[i]);
+                    //if (process.platform == "win32") { p = p.split('/').join('\\'); }
+                    try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date
+                    if ((stat != null) && (stat != undefined)) {
+                        if (stat.isDirectory() == true) {
+                            response.dir.push({ n: results[i], t: 2, d: stat.mtime });
+                        } else {
+                            response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return response;
+}
+// Delete a directory with a files and directories within it
+function deleteFolderRecursive(path, rec) {
+    if (fs.existsSync(path)) {
+        if (rec == true) {
+            fs.readdirSync(path.join(path, '*')).forEach(function (file, index) {
+                var curPath = path.join(path, file);
+                if (fs.statSync(curPath).isDirectory()) { // recurse
+                    deleteFolderRecursive(curPath, true);
+                } else { // delete file
+                    fs.unlinkSync(curPath);
+                }
+            });
+        }
+        fs.unlinkSync(path);
+    }
+};

BIN
agents/codesign.cer


+ 9 - 0
agents/compressModules.bat

@@ -0,0 +1,9 @@
+@ECHO OFF
+MD modules_meshcmd_min
+MD modules_meshcore_min
+
+"..\..\WebSiteCompiler\bin\Debug\WebSiteCompiler.exe" meshcore.js
+"..\..\WebSiteCompiler\bin\Debug\WebSiteCompiler.exe" meshcmd.js
+
+%LOCALAPPDATA%\..\Roaming\nvm\v14.16.0\node ..\translate\translate.js minifydir C:\Users\Default.DESKTOP-9CGK2DI\Desktop\AmtWebApp\meshcentral\agents\modules_meshcore C:\Users\Default.DESKTOP-9CGK2DI\Desktop\AmtWebApp\meshcentral\agents\modules_meshcore_min
+%LOCALAPPDATA%\..\Roaming\nvm\v14.16.0\node ..\translate\translate.js minifydir C:\Users\Default.DESKTOP-9CGK2DI\Desktop\AmtWebApp\meshcentral\agents\modules_meshcmd C:\Users\Default.DESKTOP-9CGK2DI\Desktop\AmtWebApp\meshcentral\agents\modules_meshcmd_min

+ 8 - 0
agents/compressRemove.bat

@@ -0,0 +1,8 @@
+@ECHO OFF
+DEL meshcmd.min.js
+DEL meshcore.min.js
+DEL modules_meshcmd_min\*.min.js
+DEL modules_meshcore_min\*.min.js
+DEL modules_meshcore_min\*.json
+RD modules_meshcmd_min
+RD modules_meshcore_min

+ 1 - 0
agents/hashagents.bat

@@ -0,0 +1 @@
+MeshService.exe hashagents.js > hashagents.json

+ 44 - 0
agents/hashagents.js

@@ -0,0 +1,44 @@
+var fs = require('fs');
+
+var agents = {
+    'MeshService.exe': 3,
+    'MeshService64.exe': 4,
+    'meshagent_x86': 5,
+    'meshagent_x86-64': 6,
+    'meshagent_arm': 9,
+    'meshagent_mips': 7,
+    'meshagent_pogo': 13,
+    'meshagent_poky': 15,
+    'meshagent_osx-x86-64': 16,
+    'meshagent_poky64': 18,
+    'meshagent_x86_nokvm': 19,
+    'meshagent_x86-64_nokvm': 20, 
+    'meshagent_arm-linaro': 24,
+    'meshagent_armhf': 25,
+    'meshagent_arm64': 26,
+    'meshagent_armhf2': 27,
+    'meshagent_mips24kc': 28,
+    'meshagent_osx-arm-64': 29,
+    'meshagent_freebsd_x86-64': 30,
+    'meshagent_aarch64': 32,
+    'meshagent_alpine-x86-64': 33,
+    'meshagent_mipsel24kc': 40,
+    'meshagent_aarch64-cortex-a53': 41,
+    // 'meshagent_armvirt32': 44,
+    'meshagent_riscv64': 45,
+    'meshagent_osx-universal-64': 10005
+}
+
+var agentinfo = {};
+for (var i in agents) {
+    var info = getAgentInfo(i, agents[i]);
+    if (info != null) { agentinfo[agents[i]] = info; }
+}
+console.log(JSON.stringify(agentinfo, null, 2));
+process.exit();
+
+function getAgentInfo(filename, id) {
+    if (fs.existsSync(filename) != true) return null;
+    var stats = fs.statSync(filename);
+    return { filename: filename, hash: getSHA384FileHash(filename).toString('hex'), size: stats.size, mtime: stats.mtime };
+}

+ 140 - 0
agents/hashagents.json

@@ -0,0 +1,140 @@
+{
+  "3": {
+    "filename": "MeshService.exe",
+    "hash": "A28899C93AE7273C49433952149AD34ADB5A017A150A27243B69402D94BF4AAC32AAE6774C40EC8DE1B4CB33A82DC1C7",
+    "size": 3811840,
+    "mtime": "2025-03-07T22:43:04Z"
+  },
+  "4": {
+    "filename": "MeshService64.exe",
+    "hash": "168572E0999089DE09B751255C908A4C2B80056D6DFAFFDB8B76F4A81847C8A4E162D6C303C14A837BBE8E6802AC6992",
+    "size": 3439616,
+    "mtime": "2025-03-07T22:43:04Z"
+  },
+  "5": {
+    "filename": "meshagent_x86",
+    "hash": "EB1F41A192A43469823399E9880EF7435259A12E2687CF488B3CB17D86BA8C29ED20DD863C86D0D5CA1D40634DEB4134",
+    "size": 3641436,
+    "mtime": "2023-10-13T16:35:50Z"
+  },
+  "6": {
+    "filename": "meshagent_x86-64",
+    "hash": "434BF98C5D4F394CB5275C73FF96CB3283DA1300577784F51AF2273DE3158074E78A351C48A9CDEA3E7DC6DECD106128",
+    "size": 3749328,
+    "mtime": "2024-08-06T15:30:40Z"
+  },
+  "7": {
+    "filename": "meshagent_mips",
+    "hash": "97F057410D979E5D290EC253B1340CDB4C1B55AFB2F766AC1CFACA368355E5D7CB86E51F44B34798F0FD1C691089859F",
+    "size": 4555960,
+    "mtime": "2023-09-19T15:16:30Z"
+  },
+  "9": {
+    "filename": "meshagent_arm",
+    "hash": "963802249E86D8D0B08D62D688E67704F519CB1D6EA03A755CF3A277D30A1579E92E863F5E31B40CBBCF8C492D3B93E8",
+    "size": 3156256,
+    "mtime": "2023-09-19T15:16:29Z"
+  },
+  "13": {
+    "filename": "meshagent_pogo",
+    "hash": "D07AD09AEBA3528785D1BE63D89785C1D2078EDA4F9E1ADE8B695A094DA4F3DB63BB16895E4BDEE733F54EF8C2F74265",
+    "size": 3164464,
+    "mtime": "2023-09-19T15:16:30Z"
+  },
+  "15": {
+    "filename": "meshagent_poky",
+    "hash": "515F9A5FFE2C229E24DB61770EEC1265F0A3AFC09C4321F1CA8D6C1A4C421FD223BDE85D94BC5B9E349EC7132CD9A7E5",
+    "size": 3804792,
+    "mtime": "2023-09-19T15:16:30Z"
+  },
+  "16": {
+    "filename": "meshagent_osx-x86-64",
+    "hash": "F7A3EBEC3D855EBFB2C72271C17196C7692EB2685DCBA70B56B63C80D6CF0DAA7DF00657BB4A12F4C0D92281B1BB47FE",
+    "size": 4670736,
+    "mtime": "2025-03-18T17:57:52Z"
+  },
+  "18": {
+    "filename": "meshagent_poky64",
+    "hash": "F889214564DBA1625DF17A7C3E14458D55350399112BAD429F8525B885EF2098EC527C3A83C3EBEF9BFB90A74AF4CD87",
+    "size": 3503608,
+    "mtime": "2023-09-19T15:16:30Z"
+  },
+  "19": {
+    "filename": "meshagent_x86_nokvm",
+    "hash": "2558D5FB2B92AF81EE914F83BB0EC528C9D10BFAB67F9F7C27BFD6AB8516B93DDCF9F824BF0F307F47254C811D707434",
+    "size": 3360704,
+    "mtime": "2023-09-19T15:16:30Z"
+  },
+  "20": {
+    "filename": "meshagent_x86-64_nokvm",
+    "hash": "A91280CAB549B1F133408F283E850F35B529218C0C317007D60A170E6113DE262521B3DA0D1ECEAC0E945B3ED46D23F7",
+    "size": 3454384,
+    "mtime": "2023-09-19T15:16:30Z"
+  },
+  "24": {
+    "filename": "meshagent_arm-linaro",
+    "hash": "47E5DDAD71536DBB7752C665A4FE3BDD98D2AB888DB7536FB58E2EC7F0560C750AFE2D2D7F35A00457677661312380E6",
+    "size": 2215252,
+    "mtime": "2023-09-19T15:16:29Z"
+  },
+  "25": {
+    "filename": "meshagent_armhf",
+    "hash": "65F6FE3B530FDBC3C1E7B2577B081FEF5742265CCCFDE27AB015996D3767F153CF3B4B022932C156754E09CDA2EE4874",
+    "size": 3191132,
+    "mtime": "2023-09-19T15:16:29Z"
+  },
+  "27": {
+    "filename": "meshagent_armhf2",
+    "hash": "0AE840520D3B677B9767EA097F3AA5A1E24212529E688200F43935DB1541AB9FB441EC2C7BA8002D45299B04695FD037",
+    "size": 2837724,
+    "mtime": "2023-09-19T15:16:29Z"
+  },
+  "28": {
+    "filename": "meshagent_mips24kc",
+    "hash": "3AD0A21E1FDD44E1869BFD3E3698D67713445C7A21BEE37AF41E810E6B6F1CE8FE369140AE7BAD31689A5F759CF7BCC6",
+    "size": 4193384,
+    "mtime": "2023-09-19T15:16:30Z"
+  },
+  "29": {
+    "filename": "meshagent_osx-arm-64",
+    "hash": "CFE022146F2ED61E68F907E57E3704CC7F409D7F2B4D87E64ED6D83C53F41777BCDDCCF42DF8B8BB25CCEC9A93D799C7",
+    "size": 3945576,
+    "mtime": "2025-03-12T11:13:54Z"
+  },
+  "30": {
+    "filename": "meshagent_freebsd_x86-64",
+    "hash": "5C2CDDA2E7AB5068D990FBC725D8D5E3EA2724A0E001C226C0C7BB9F3A46492880BF260B5DD9E733F87EB68BA7494BD6",
+    "size": 4673560,
+    "mtime": "2023-09-19T15:16:29Z"
+  },
+  "32": {
+    "filename": "meshagent_aarch64",
+    "hash": "56AD2FAFBC15BAC635C526C6F106DD0213C0B222265685F00CDD56AD8C3DFE7DE6C4D8F52EA9CA183D50BC8D8C198477",
+    "size": 3256688,
+    "mtime": "2023-09-19T15:16:29Z"
+  },
+  "40": {
+    "filename": "meshagent_mipsel24kc",
+    "hash": "E6A3CDBFFD233BE1B24E81197F41DB24EFA1708B5BD18B2D7DFEBFD89FAF6A994B5D4A3048D67BBBAB14A77AD7088AE6",
+    "size": 4189672,
+    "mtime": "2023-09-19T15:16:30Z"
+  },
+  "41": {
+    "filename": "meshagent_aarch64-cortex-a53",
+    "hash": "CEFD8AAB52CE01939324E999E1EB541A262E0D4F1C3591F0A8355EAD5D2C87B43B3FB05A1DCD04012CDD4B559589CBAB",
+    "size": 3084616,
+    "mtime": "2023-09-19T15:16:29Z"
+  },
+  "45": {
+    "filename": "meshagent_riscv64",
+    "hash": "CFB8C78CB128B5F8FE367DE40ACD1D34BB9E261B8B37AF867F0B50130F847831B8C0DB4C9DAFC75F5DD80AFECCE74D30",
+    "size": 3597928,
+    "mtime": "2025-03-18T11:06:16Z"
+  },
+  "10005": {
+    "filename": "meshagent_osx-universal-64",
+    "hash": "D320CA61D59FD8D76CF681CFE78A94CE37C47DBCAA8B29DF483F42C000EA9B655B5E5909A2AD6699D45D2D7691FF4964",
+    "size": 8647784,
+    "mtime": "2024-03-15T14:55:06Z"
+  }
+}

BIN
agents/meshagent_aarch64


BIN
agents/meshagent_aarch64-cortex-a53


BIN
agents/meshagent_android.apk


BIN
agents/meshagent_arm


BIN
agents/meshagent_arm-linaro


BIN
agents/meshagent_armhf


BIN
agents/meshagent_armhf2


BIN
agents/meshagent_freebsd_x86-64


BIN
agents/meshagent_linux-armada370-hf


BIN
agents/meshagent_mips


BIN
agents/meshagent_mips24kc


BIN
agents/meshagent_mipsel24kc


BIN
agents/meshagent_openbsd_x86-64


BIN
agents/meshagent_openwrt_x86_64


BIN
agents/meshagent_osx-arm-64


BIN
agents/meshagent_osx-universal-64


BIN
agents/meshagent_osx-x86-64


BIN
agents/meshagent_pogo


BIN
agents/meshagent_poky


BIN
agents/meshagent_poky64


BIN
agents/meshagent_riscv64


BIN
agents/meshagent_x86


BIN
agents/meshagent_x86-64


BIN
agents/meshagent_x86-64_nokvm


BIN
agents/meshagent_x86_nokvm


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 52 - 0
agents/meshcmd.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 578 - 0
agents/meshcore.js


+ 206 - 0
agents/meshcore_diagnostic.js

@@ -0,0 +1,206 @@
+/*
+Copyright 2019 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+require('MeshAgent').on('Connected', function (status)
+{
+    if (status == 0)
+    {
+        return;
+    }
+    this.timeout = setTimeout(start, 10000);
+});
+
+
+
+function sendServerLog(msg)
+{
+    require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'log', value: msg } });
+}
+function getMeshAgentService()
+{
+    try
+    {
+        var ret = require('service-manager').manager.getService(process.platform == 'win32' ? 'mesh agent' : 'meshagent');
+        return(ret);
+    }
+    catch(e)
+    {
+        return (null);
+    }
+}
+
+function getARCHID() {
+    var ret = 0;
+    switch (process.platform) {
+        case 'linux':
+            // Need to detect Architecture ID
+            var child = require('child_process').execFile('/bin/sh', ['sh']);
+            child.stdout.str = '';
+            child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
+            child.stdin.write("uname -m\nexit\n");
+            child.waitExit();
+            switch (child.stdout.str.trim()) {
+                case 'x86_64':
+                case 'amd64':
+                    ret = 6;
+                    break;
+                case 'x86':
+                case 'i686':
+                case 'i586':
+                case 'i386':
+                    ret = 5;
+                    break;
+                case 'armv6l':
+                case 'armv7l':
+                    ret = 25;
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case 'darwin':
+            ret = 16;
+            break;
+        case 'win32':
+            ret = process.arch == 'x64' ? 4 : 3;
+            break;
+    }
+    return (ret);
+}
+
+function DownloadAgentBinary(path, ID)
+{
+    var options = require('http').parseUri(require('MeshAgent').ServerInfo.ServerUri);
+    var downloadUri = 'https://' + options.host + ':' + options.port + '/meshagents?id=' + (ID != null ? ID : getARCHID());
+    sendServerLog('Diagnostic: Attempting to downlod agent from: ' + downloadUri);
+
+    return (wget(downloadUri, path, { rejectUnauthorized: false }));
+}
+
+function giveup()
+{
+    sendServerLog('Diagnostic: Unable to diagnose Mesh Agent');
+    finished();
+}
+function finished()
+{
+    sendServerLog('Diagnostic: End');
+    require('service-manager').manager.getService('meshagentDiagnostic').stop();
+}
+
+function ConfigureAgent(agent)
+{
+    sendServerLog('...Configuring Agent...');
+    var info = require('MeshAgent').ServerInfo;
+
+    var msh = 'MeshID=0x' + info.MeshID + '\n' + 'ServerID=' + info.ServerID + '\n' + 'MeshServer=' + info.ServerUri + '\n';
+    var cfg = require('global-tunnel').proxyConfig;
+    if(cfg == null)
+    {
+        msh += 'ignoreProxyFile=1\n';
+    }
+    else
+    {
+        msh += ('WebProxy=' + cfg.host + ':' + cfg.port + '\n');
+    }
+    if(process.platform == 'win32')
+    {
+        require('fs').writeFileSync(agent.appLocation().replace('.exe', '.msh'), msh);
+    }
+    else
+    {
+        require('fs').writeFileSync(agent.appLocation() + '.msh', msh);
+    }
+}
+
+function start()
+{
+    sendServerLog('Diagnostic: Start');
+
+    var id = getARCHID();
+    var s = getMeshAgentService();
+    if (s == null)
+    {
+        DownloadAgentBinary('agent_temporary.bin').then(function ()
+        {
+            // SUCCESS
+            try
+            {
+                var agent = require('service-manager').manager.installService(
+                    {
+                        name: process.platform == 'win32' ? 'Mesh Agent' : 'meshagent',
+                        target: 'meshagent',
+                        description: 'Mesh Central Agent v2 Background Service',
+                        displayName: 'Mesh Agent v2 Background Service',
+                        servicePath: 'agent_temporary.bin',
+                        startType: 'DEMAND_START'
+                    });
+                require('fs').unlinkSync('agent_temporary.bin');
+                ConfigureAgent(agent);
+            }
+            catch(e)
+            {
+                giveup();
+            }
+        },
+        function ()
+        {
+            // FAILURE
+            giveup();
+        });
+    }
+    if(s!=null)
+    {
+        // Mesh Agent Installation Found
+        sendServerLog('Diagnostic: Mesh Agent Service => ' + (s.isRunning() ? 'RUNNING' : 'NOT-RUNNING'));
+        if(s.isRunning())
+        {
+            finished();
+        }
+        else
+        {
+            sendServerLog('Diagnostic: Attempting to start Mesh Agent');
+            s.start();
+            sendServerLog('Diagnostic: ' + (s.isRunning() ? '(SUCCESS)' : '(FAILED)'));
+            if (s.isRunning())
+            {
+                finished();
+                return;
+            }
+            else
+            {
+                DownloadAgentBinary(s.appLocation()).then(
+                    function () {
+                        sendServerLog('Diagnostic: Downloaded Successfully');
+                        sendServerLog('Diagnostic: Attempting to start Mesh Agent');
+                        s.start();
+                        sendServerLog('Diagnostic: ' + (s.isRunning() ? '(SUCCESS)' : '(FAILED)'));
+                        if (s.isRunning()) {
+                            finished();
+                            return;
+                        }
+                        else {
+                            giveup();
+                        }
+                    },
+                    function () {
+                        sendServerLog('Diagnostic: Download Failed');
+                        giveup();
+                    });
+            }
+        }
+    }
+};

+ 17 - 0
agents/meshinstall-bsd-rcd.sh

@@ -0,0 +1,17 @@
+#!/bin/sh
+# PROVIDE: meshagent
+# REQUIRE: FILESYSTEMS NETWORKING
+# KEYWORD: shutdown
+. /etc/rc.subr
+
+name="meshagent"
+desc="MeshCentral Agent"
+rcvar=${name}_enable
+pidfile="/var/run/meshagent.pid"
+meshagent_chdir="/usr/local/mesh"
+command="/usr/sbin/daemon"
+command_args="-P ${pidfile} -r -f /usr/local/mesh/meshagent "
+
+load_rc_config $name
+: ${meshagent_enable="YES"}
+run_rc_command "$1"

+ 84 - 0
agents/meshinstall-initd.sh

@@ -0,0 +1,84 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides:          <NAME>
+# Required-Start:    $local_fs $network $named $time $syslog
+# Required-Stop:     $local_fs $network $named $time $syslog
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Description:       <DESCRIPTION>
+### END INIT INFO
+
+SCRIPT=/usr/local/mesh/meshagent
+RUNAS=root
+
+PIDFILE=/var/run/meshagent.pid
+LOGFILE=/var/log/meshagent.log
+
+start() {
+  if [ -f "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") 2>/dev/null; then
+    echo 'Service already running' >&2
+    return 1
+  fi
+  echo 'Starting service…' >&2
+  local CMD="$SCRIPT -exec \"var child; process.on('SIGTERM', function () { child.removeAllListeners('exit'); child.kill(); process.exit(); }); function start() { child = require('child_process').execFile(process.execPath, [process.argv0, \"\"]); child.stdout.on('data', function (c) { }); child.stderr.on('data', function (c) { }); child.on('exit', function (status) { start(); }); } start();\" &> \"$LOGFILE\" & echo \$!"
+
+  cd /usr/local/mesh
+  su -c "$CMD" $RUNAS > "$PIDFILE"
+  echo 'Service started' >&2
+}
+
+stop() {
+  if [ ! -f "$PIDFILE" ]; then
+    echo 'Service not running' >&2
+    return 1
+  else
+    pid=$( cat "$PIDFILE" )
+    if kill -0 $pid 2>/dev/null; then
+          echo 'Stopping service…' >&2
+          kill -15 $pid
+          echo 'Service stopped' >&2
+    else
+      echo 'Service not running'
+    fi
+    rm -f $"PIDFILE"
+  fi
+}
+restart(){
+    stop
+    start
+}
+status(){
+    if [ -f "$PIDFILE" ]
+    then
+        pid=$( cat "$PIDFILE" )
+        if kill -0 $pid 2>/dev/null; then
+            echo "meshagent start/running, process $pid"
+        else
+            echo 'meshagent stop/waiting'
+        fi
+    else
+        echo 'meshagent stop/waiting'
+    fi
+
+}
+
+
+case "$1" in
+    start)
+        start
+        ;;
+    stop)
+        stop
+        ;;
+    restart)
+        stop
+        start
+        ;;
+    status)
+        status
+        ;;
+    *)
+        echo "Usage: service meshagent {start|stop|restart|status}"
+        ;;
+esac
+exit 0

+ 361 - 0
agents/meshinstall-linux.js

@@ -0,0 +1,361 @@
+/*
+Copyright 2020-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+Object.defineProperty(Array.prototype, 'getParameterEx',
+        {
+            value: function (name, defaultValue)
+            {
+                var i, ret;
+                for (i = 0; i < this.length; ++i)
+                {
+                    if (this[i] == name) { return (null); }
+                    if (this[i].startsWith(name + '='))
+                    {
+                        ret = this[i].substring(name.length + 1);
+                        if (ret.startsWith('"')) { ret = ret.substring(1, ret.length - 1); }
+                        return (ret);
+                    }
+                }
+                return (defaultValue);
+            }
+        });
+Object.defineProperty(Array.prototype, 'getParameter',
+    {
+        value: function (name, defaultValue)
+        {
+            return (this.getParameterEx('-' + name, defaultValue));
+        }
+    });
+
+// The following line just below with 'msh=' needs to stay exactly like this since MeshCentral will replace it with the correct settings.
+var msh = {};
+var translation = JSON.parse(msh.translation);
+
+var lang = require('util-language').current;
+if (lang == null) { lang = 'en'; }
+if (lang == "C"){
+    lang = 'en';
+    console.log("Langauge envronment variable was not set (process.env.LANG).  Defaulting to English ('en').\nSee the agent-translations.json file for a list of current languages that are implemented\nUsage: meshcentral -lang=en\n");    
+}
+if (process.argv.getParameter('lang', lang) == null)
+{
+    console.log('\nCurrent Language: ' + lang + '\n');
+    process.exit();
+}
+else
+{
+    lang = process.argv.getParameter('lang', lang).toLowerCase();
+    lang = lang.split('_').join('-');
+    if (translation[lang] == null)
+    {
+        if (translation[lang.split('-')[0]] == null)
+        {
+            console.log('Language: ' + lang + ' is not translated.');
+            console.log("try: './"+ process.execPath.split('/').pop() + " -lang=en' for English");
+            console.log("See the agent-translations.json file for a list of current languages that are implemented.")
+            process.exit();
+        }
+        else
+        {
+            lang = lang.split('-')[0];
+        }
+    }
+}
+
+if (lang != 'en')
+{
+    for (var i in translation['en'])
+    {
+        // If translated entries are missing, substitute the english translation
+        if (translation[lang][i] == null) { translation[lang][i] = translation['en'][i]; }
+    }
+}
+
+
+var displayName = msh.displayName ? msh.displayName : 'MeshCentral Agent';
+var s = null, buttons = [translation[lang].cancel], skip = false;
+var serviceName = msh.meshServiceName ? msh.meshServiceName : 'meshagent';
+
+try { s = require('service-manager').manager.getService(serviceName); } catch (e) { }
+
+var connectArgs = [process.execPath.split('/').pop(), '--no-embedded=1', '--disableUpdate=1'];
+connectArgs.push('--MeshName="' + msh.MeshName + '"');
+connectArgs.push('--MeshType="' + msh.MeshType + '"');
+connectArgs.push('--MeshID="' + msh.MeshID + '"');
+connectArgs.push('--ServerID="' + msh.ServerID + '"');
+connectArgs.push('--MeshServer="' + msh.MeshServer + '"');
+connectArgs.push('--AgentCapabilities="0x00000020"');
+if (msh.displayName) { connectArgs.push('--displayName="' + msh.displayName + '"'); }
+if (msh.agentName) { connectArgs.push('--agentName="' + msh.agentName + '"'); }
+
+function _install(parms)
+{
+    var i;
+    var mstr = require('fs').createWriteStream(process.execPath + '.msh', { flags: 'wb' });
+
+    for (i in msh)
+    {
+        mstr.write(i + '=' + msh[i] + '\n');
+    }
+    mstr.end();
+
+    if (parms == null) { parms = []; }
+    if (msh.companyName) { parms.unshift('--companyName="' + msh.companyName + '"'); }
+    if (msh.displayName) { parms.unshift('--displayName="' + msh.displayName + '"'); }
+    if (msh.meshServiceName) { parms.unshift('--meshServiceName="' + msh.meshServiceName + '"'); }
+    parms.unshift('--copy-msh=1');
+    parms.unshift('--no-embedded=1');
+    parms.unshift('-fullinstall');
+    parms.unshift(process.execPath.split('/').pop());
+
+    global._child = require('child_process').execFile(process.execPath, parms);
+    global._child.stdout.on('data', function (c) { process.stdout.write(c.toString()); });
+    global._child.stderr.on('data', function (c) { process.stdout.write(c.toString()); });
+    global._child.waitExit();
+}
+
+function _uninstall()
+{
+    global._child = require('child_process').execFile(process.execPath,
+            [process.execPath.split('/').pop(), '-fulluninstall', '--no-embedded=1', '--meshServiceName="' + serviceName + '"']);
+
+    global._child.stdout.on('data', function (c) { process.stdout.write(c.toString()); });
+    global._child.stderr.on('data', function (c) { process.stdout.write(c.toString()); });
+    global._child.waitExit();
+}
+
+if (msh.InstallFlags == null)
+{
+    msh.InstallFlags = 3;
+} else
+{
+    msh.InstallFlags = parseInt(msh.InstallFlags.toString());
+}
+
+if (process.argv.includes('-mesh'))
+{
+    console.log(JSON.stringify(msh, null, 2));
+    process.exit();
+}
+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 && process.argv.length == 1))
+{
+    console.log("\n" + translation[lang].commands + ": ");
+    if ((msh.InstallFlags & 1) == 1)
+    {
+        console.log('./' + process.execPath.split('/').pop() + ' -connect');
+    }
+    if ((msh.InstallFlags & 2) == 2)
+    {
+        if (s)
+        {
+            console.log('./' + process.execPath.split('/').pop() + ' -update');
+            console.log('./' + process.execPath.split('/').pop() + ' -uninstall');
+        }
+        else
+        {
+            console.log('./' + process.execPath.split('/').pop() + ' -install');
+            console.log('./' + process.execPath.split('/').pop() + ' -install --installPath="/alternate/path"');
+        }
+    }
+    console.log('');
+    process.exit();
+}
+
+if ((msh.InstallFlags & 1) == 1)
+{
+    buttons.unshift(translation[lang].connect);
+    if (process.argv.includes('-connect'))
+    {
+        global._child = require('child_process').execFile(process.execPath, connectArgs);
+        global._child.stdout.on('data', function (c) { });
+        global._child.stderr.on('data', function (c) { });
+        global._child.on('exit', function (code) { process.exit(code); });
+
+        console.log("\n" + translation[lang].url + ": " + msh.MeshServer);
+        console.log(translation[lang].group + ": " + msh.MeshName);
+        console.log('\n' + translation[lang].ctrlc + '\n');
+        skip = true;
+    }
+}
+
+if ((!skip) && ((msh.InstallFlags & 2) == 2))
+{
+    if (!require('user-sessions').isRoot())
+    {
+        console.log('\n' + translation[lang].elevation);
+        console.log(translation[lang].sudo);
+        process.exit();
+    }
+    if (s)
+    {
+        if ((process.platform == 'darwin') || require('message-box').kdialog)
+        {
+            buttons.unshift(translation[lang].setup);
+        } else
+        {
+            buttons.unshift(translation[lang].uninstall);
+            buttons.unshift(translation[lang].update);
+        }
+    } else
+    {
+        buttons.unshift(translation[lang].install);
+    }
+}
+
+    if (!skip)
+    {
+        if (process.argv.includes('-install') || process.argv.includes('-update'))
+        {
+            var p = [];
+            for (var i = 0; i < process.argv.length; ++i)
+            {
+                if (process.argv[i].startsWith('--installPath='))
+                {
+                    p.push('--installPath="' + process.argv[i].split('=').pop() + '"');
+                }
+                else if(process.argv[i].startsWith('--'))
+                {
+                    p.push(process.argv[i]);
+                }
+            }
+            _install(p);
+            process.exit();
+        }
+        else if (process.argv.includes('-uninstall'))
+        {
+            _uninstall();
+            process.exit();
+        }
+        else
+        {
+            if (!require('message-box').kdialog && ((require('message-box').zenity == null) || (!require('message-box').zenity.extra)))
+            {
+                console.log('\n' + translation[lang].graphicalerror + '.');
+                console.log(translation[lang].zenity + ".\n");
+                console.log(translation[lang].commands + ": ");
+                if ((msh.InstallFlags & 1) == 1)
+                {
+                    console.log('./' + process.execPath.split('/').pop() + ' -connect');
+                }
+                if ((msh.InstallFlags & 2) == 2)
+                {
+                    if (s)
+                    {
+                        console.log('./' + process.execPath.split('/').pop() + ' -update');
+                        console.log('./' + process.execPath.split('/').pop() + ' -uninstall');
+                    }
+                    else
+                    {
+                        console.log('./' + process.execPath.split('/').pop() + ' -install');
+                        console.log('./' + process.execPath.split('/').pop() + ' -install --installPath="/alternate/path"');
+                    }
+                }
+                console.log('');
+                process.exit();
+            }
+        }
+        if (process.platform == 'darwin')
+        {
+            if (!require('user-sessions').isRoot()) { console.log('\n' + translation[lang].elevation); process.exit(); }
+        }
+    }
+
+
+if (!skip)
+{
+    if (!s)
+    {
+        msg = translation[lang].agent + ": " + translation[lang].status[0] + '\n';
+    } else
+    {
+        msg = translation[lang].agent + ": " + (s.isRunning() ? translation[lang].status[1] : translation[lang].status[2]) + '\n';
+    }
+
+    msg += (translation[lang].group + ": " + msh.MeshName + '\n');
+    msg += (translation[lang].url + ": " + msh.MeshServer + '\n');
+
+    var p = require('message-box').create(displayName + " " + translation[lang].setup, msg, 99999, buttons);
+    p.then(function (v)
+    {
+        switch (v)
+        {
+            case translation[lang].cancel:
+                process.exit();
+                break;
+            case translation[lang].setup:
+                var d = require('message-box').create(displayName, msg, 99999, [translation[lang].update, translation[lang].uninstall, translation[lang].cancel]);
+                d.then(function (v)
+                {
+                    switch (v)
+                    {
+                        case translation[lang].update:
+                        case translation[lang].install:
+                            _install();
+                            break;
+                        case translation[lang].uninstall:
+                            _uninstall();
+                            break;
+                        default:
+                            break;
+                    }
+                    process.exit();
+                }).catch(function (v) { process.exit(); });
+                break;
+            case translation[lang].connect:
+                global._child = require('child_process').execFile(process.execPath, connectArgs);
+                global._child.stdout.on('data', function (c) { });
+                global._child.stderr.on('data', function (c) { });
+                global._child.on('exit', function (code) { process.exit(code); });
+
+                msg = (translation[lang].group + ": " + msh.MeshName + '\n');
+                msg += (translation[lang].url + ": " + msh.MeshServer + '\n');
+
+                if (process.platform != 'darwin')
+                {
+                    if (!require('message-box').zenity && require('message-box').kdialog)
+                    {
+                        msg += ('\n' + translation[lang].pressok);
+                    }
+                }
+
+                var d = require('message-box').create(displayName, msg, 99999, [translation[lang].disconnect]);
+                d.then(function (v) { process.exit(); }).catch(function (v) { process.exit(); });
+                break;
+            case translation[lang].uninstall:
+                _uninstall();
+                process.exit();
+                break;
+            case translation[lang].install:
+            case translation[lang].update:
+                _install();
+                process.exit();
+                break;
+            default:
+                console.log(v);
+                process.exit();
+                break;
+        }
+    }).catch(function (e)
+    {
+        console.log(e);
+        process.exit();
+    });
+}

+ 210 - 0
agents/meshinstall-linux.sh

@@ -0,0 +1,210 @@
+#!/usr/bin/env bash
+
+CheckStartupType() {
+  # 1 = Systemd
+  # 2 = Upstart
+  # 3 = init.d
+  # 5 = BSD
+
+  # echo "Checking if Linux or BSD Platform"
+  plattype=`uname | awk '{ tst=tolower($0);a=split(tst, res, "bsd"); if(a==1) { print "LINUX"; } else { print "BSD"; }}'`
+  if [[ $plattype == 'BSD' ]]
+   then return 5;
+  fi
+
+  # echo "Checking process autostart system..."
+  starttype1=`cat /proc/1/status | grep 'Name:' | awk '{ print $2; }'`
+  starttype2=`ps -p 1 -o command= | awk '{a=split($0,res," "); b=split(res[a],tp,"/"); print tp[b]; }'`
+ 
+  # Systemd
+  if [[ $starttype1 == 'systemd' ]]
+    then return 1;
+  elif [[ $starttype1 == 'init'  ||  $starttype2 == 'init' ]]
+    then
+        if [ -d "/etc/init" ]
+            then
+                return 2;
+            else
+                return 3;
+        fi
+  fi
+  return 0;
+}
+
+
+# Add "StartupType=(type)" to .msh file
+UpdateMshFile() {
+  # Remove all lines that start with "StartupType="
+  sed '/^StartupType=/ d' < ./meshagent.msh >> ./meshagent2.msh
+  # Add the startup type to the file
+  echo "StartupType=$starttype" >> ./meshagent2.msh
+  mv ./meshagent2.msh ./meshagent.msh
+}
+
+CheckInstallAgent() {
+  # echo "Checking mesh identifier..."
+  if [ -e "/usr/local" ]
+  then
+    installpath="/usr/local/mesh"
+  else
+    installpath="/usr/mesh"
+  fi
+  if [ $# -ge 2 ]
+  then
+    uninstall=$1
+    url=$2
+    meshid=$3
+    if [[ $4 =~ ^--WebProxy= ]];
+    then
+       webproxy=$4
+    fi
+
+
+
+    meshidlen=${#meshid}
+    if [ $meshidlen -gt 63 ]
+    then
+      machineid=0
+      machinetype=$( uname -m )
+
+      # If we have 3 arguments...
+      if [ $# -ge 4 ] &&  [ -z "$webproxy" ]
+      then
+        # echo "Computer type is specified..."
+        machineid=$4
+      else
+        # echo "Detecting computer type..."
+        if [ $machinetype == 'x86_64' ] || [ $machinetype == 'amd64' ]
+        then
+          if [ $starttype -eq 5 ]
+          then
+            # FreeBSD x86, 64 bit
+            machineid=30
+          else
+            # Linux x86, 64 bit
+            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' ]
+        then
+          if [ $starttype -eq 5 ]
+          then
+            # FreeBSD x86, 32 bit
+            machineid=31
+          else
+            # Linux x86, 32 bit
+            machineid=5
+          fi
+        fi
+        if [ $machinetype == 'armv6l' ] || [ $machinetype == 'armv7l' ]
+        then
+          # RaspberryPi 1 (armv6l) or RaspberryPi 2/3 (armv7l)
+          machineid=25
+        fi
+        if [ $machinetype == 'aarch64' ]
+        then
+          # RaspberryPi 3B+ running Ubuntu 64 (aarch64)
+          machineid=26
+        fi
+        if [ $machinetype == 'riscv64' ]
+        then
+          # RISC-V 64 bit
+          machineid=45
+        fi
+        # Add more machine types, detect KVM support... here.
+      fi
+
+      if [ $machineid -eq 0 ]
+      then
+        echo "Unsupported machine type: $machinetype."
+      else
+        DownloadAgent $uninstall $url $meshid $machineid
+      fi
+
+    else
+      echo "Device group identifier is not correct, must be at least 64 characters long."
+    fi
+  else
+    echo "URI and/or device group identifier have not been specified, must be passed in as arguments."
+    return 0;
+  fi
+}
+
+DownloadAgent() {
+  uninstall=$1
+  url=$2
+  meshid=$3
+  machineid=$4
+  echo "Downloading agent #$machineid..."
+  wget $url/meshagents?id=$machineid {{{wgetoptionshttps}}}-O ./meshagent || curl {{{curloptionshttps}}}--output ./meshagent $url/meshagents?id=$machineid
+
+  # If it did not work, try again using http
+  if [ $? != 0 ]
+  then
+    url=${url/"https://"/"http://"}
+    wget $url/meshagents?id=$machineid {{{wgetoptionshttp}}}-O ./meshagent || curl {{{curloptionshttp}}}--output ./meshagent $url/meshagents?id=$machineid
+  fi
+
+  if [ $? -eq 0 ]
+  then
+    echo "Agent downloaded."
+    # TODO: We could check the meshagent sha256 hash, but best to authenticate the server.
+    chmod 755 ./meshagent
+    wget $url/meshsettings?id=$meshid {{{wgetoptionshttps}}}-O ./meshagent.msh || curl {{{curloptionshttps}}}--output ./meshagent.msh $url/meshsettings?id=$meshid
+
+    # If it did not work, try again using http
+    if [ $? -ne 0 ]
+    then
+      wget $url/meshsettings?id=$meshid {{{wgetoptionshttp}}}-O ./meshagent.msh || curl {{{curloptionshttp}}}--output ./meshagent.msh $url/meshsettings?id=$meshid
+    fi
+
+    if [ $? -eq 0 ]
+    then
+      # Update the .msh file and run the agent installer/uninstaller
+      if [ $uninstall == 'uninstall' ] || [ $uninstall == 'UNINSTALL' ]
+      then
+        # Uninstall the agent
+        ./meshagent -fulluninstall
+      else
+        # Install the agent
+        UpdateMshFile
+        ./meshagent -fullinstall --copy-msh=1 $webproxy
+      fi
+    else
+      echo "Unable to download device group settings at: $url/meshsettings?id=$meshid."
+    fi
+  else
+    echo "Unable to download agent at: $url/meshagents?id=$machineid."
+  fi
+}
+
+
+CheckStartupType
+starttype=$?
+#echo "Type: $starttype"
+
+currentuser=$( whoami )
+if [ $currentuser == 'root' ]
+then
+  if [ $# -eq 0 ]
+  then
+    echo -e "This script will install or uninstall a agent, usage:\n  $0 [serverUrl] [deviceGroupId] (machineId)\n  $0 uninstall [serverUrl] [deviceGroupId] (machineId)"
+  else
+    if [ $1 == 'uninstall' ] || [ $1 == 'UNINSTALL' ]
+    then
+      CheckInstallAgent 'uninstall' $2 $3 $4
+    else
+      CheckInstallAgent 'install' $1 $2 $3
+    fi
+  fi
+else
+  echo "Must be root to install or uninstall the agent."
+fi

+ 459 - 0
agents/modules_meshcmd/amt-apfclient.js

@@ -0,0 +1,459 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/**
+* @description APF/CIRA Client for Duktape
+* @author Joko Sastriawan & Ylian Saint-Hilaire
+* @copyright Intel Corporation 2020-2021
+* @license Apache-2.0
+* @version v0.0.2
+*/
+
+function CreateAPFClient(parent, args) {
+    if ((args.clientuuid == null) || (args.clientuuid.length != 36)) return null; // Require a UUID if this exact length
+
+    var obj = {};
+    obj.parent = parent;
+    obj.args = args;
+    obj.http = require('http');
+    obj.net = require('net');
+    obj.forwardClient = null;
+    obj.downlinks = {};
+    obj.pfwd_idx = 0;
+    obj.timer = null; // Keep alive timer
+
+    // obj.onChannelClosed
+    // obj.onJsonControl
+
+    // Function copied from common.js
+    function ReadInt(v, p) { return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); }; // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
+    function IntToStr(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); };
+    function hex2rstr(d) { var r = '', m = ('' + d).match(/../g), t; while (t = m.shift()) { r += String.fromCharCode('0x' + t); } return r; };
+    function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }; // Convert decimal to hex
+    function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }; // Convert a raw string to a hex string
+    function d2h(d) { return (d / 256 + 1 / 512).toString(16).substring(2, 4); }
+    function buf2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += d2h(input[i]); } return r; };
+    function Debug(str) { if (obj.parent.debug) { console.log(str); } }
+    function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + "-" + g.substring(10, 12) + g.substring(8, 10) + "-" + g.substring(14, 16) + g.substring(12, 14) + "-" + g.substring(16, 20) + "-" + g.substring(20); }
+    function strToGuid(s) { s = s.replace(/-/g, ''); var ret = s.substring(6, 8) + s.substring(4, 6) + s.substring(2, 4) + s.substring(0, 2) + s.substring(10, 12) + s.substring(8, 10) + s.substring(14, 16) + s.substring(12, 14) + s.substring(16, 20) + s.substring(20); return ret; }
+    function binzerostring(len) { var res = ''; for (var l = 0; l < len; l++) { res += String.fromCharCode(0 & 0xFF); } return res; }
+
+    // CIRA state
+    var CIRASTATE = {
+        INITIAL: 0,
+        PROTOCOL_VERSION_SENT: 1,
+        AUTH_SERVICE_REQUEST_SENT: 2,
+        AUTH_REQUEST_SENT: 3,
+        PFWD_SERVICE_REQUEST_SENT: 4,
+        GLOBAL_REQUEST_SENT: 5,
+        FAILED: -1
+    }
+    obj.cirastate = CIRASTATE.INITIAL;
+
+    // REDIR state
+    var REDIR_TYPE = {
+        REDIR_UNKNOWN: 0,
+        REDIR_SOL: 1,
+        REDIR_KVM: 2,
+        REDIR_IDER: 3
+    }
+
+    // redirection start command
+    obj.RedirectStartSol = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x53, 0x4F, 0x4C, 0x20);
+    obj.RedirectStartKvm = String.fromCharCode(0x10, 0x01, 0x00, 0x00, 0x4b, 0x56, 0x4d, 0x52);
+    obj.RedirectStartIder = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x49, 0x44, 0x45, 0x52);
+
+    // Intel AMT forwarded port list for non-TLS mode
+    //var pfwd_ports = [16992, 623, 16994, 5900];
+    var pfwd_ports = [ 16992, 16993 ];
+
+    // protocol definitions
+    var APFProtocol = {
+        UNKNOWN: 0,
+        DISCONNECT: 1,
+        SERVICE_REQUEST: 5,
+        SERVICE_ACCEPT: 6,
+        USERAUTH_REQUEST: 50,
+        USERAUTH_FAILURE: 51,
+        USERAUTH_SUCCESS: 52,
+        GLOBAL_REQUEST: 80,
+        REQUEST_SUCCESS: 81,
+        REQUEST_FAILURE: 82,
+        CHANNEL_OPEN: 90,
+        CHANNEL_OPEN_CONFIRMATION: 91,
+        CHANNEL_OPEN_FAILURE: 92,
+        CHANNEL_WINDOW_ADJUST: 93,
+        CHANNEL_DATA: 94,
+        CHANNEL_CLOSE: 97,
+        PROTOCOLVERSION: 192,
+        KEEPALIVE_REQUEST: 208,
+        KEEPALIVE_REPLY: 209,
+        KEEPALIVE_OPTIONS_REQUEST: 210,
+        KEEPALIVE_OPTIONS_REPLY: 211,
+        JSON_CONTROL: 250 // This is a Mesh specific command that sends JSON to and from the MPS server.
+    }
+
+    var APFDisconnectCode = {
+        HOST_NOT_ALLOWED_TO_CONNECT: 1,
+        PROTOCOL_ERROR: 2,
+        KEY_EXCHANGE_FAILED: 3,
+        RESERVED: 4,
+        MAC_ERROR: 5,
+        COMPRESSION_ERROR: 6,
+        SERVICE_NOT_AVAILABLE: 7,
+        PROTOCOL_VERSION_NOT_SUPPORTED: 8,
+        HOST_KEY_NOT_VERIFIABLE: 9,
+        CONNECTION_LOST: 10,
+        BY_APPLICATION: 11,
+        TOO_MANY_CONNECTIONS: 12,
+        AUTH_CANCELLED_BY_USER: 13,
+        NO_MORE_AUTH_METHODS_AVAILABLE: 14,
+        INVALID_CREDENTIALS: 15,
+        CONNECTION_TIMED_OUT: 16,
+        BY_POLICY: 17,
+        TEMPORARILY_UNAVAILABLE: 18
+    }
+
+    var APFChannelOpenFailCodes = {
+        ADMINISTRATIVELY_PROHIBITED: 1,
+        CONNECT_FAILED: 2,
+        UNKNOWN_CHANNEL_TYPE: 3,
+        RESOURCE_SHORTAGE: 4,
+    }
+
+    var APFChannelOpenFailureReasonCode = {
+        AdministrativelyProhibited: 1,
+        ConnectFailed: 2,
+        UnknownChannelType: 3,
+        ResourceShortage: 4,
+    }
+
+    obj.onSecureConnect = function onSecureConnect(resp, ws, head) {
+        Debug("APF Secure WebSocket connected.");
+        //console.log(JSON.stringify(resp));                
+        obj.forwardClient.tag = { accumulator: [] };
+        obj.forwardClient.ws = ws;
+        obj.forwardClient.ws.on('end', function () {
+            Debug("APF: Connection is closing.");
+            if (obj.timer != null) { clearInterval(obj.timer); obj.timer = null; }
+            if (obj.onChannelClosed) { obj.onChannelClosed(obj); }
+        });
+
+        obj.forwardClient.ws.on('data', function (data) {
+            obj.forwardClient.tag.accumulator += hex2rstr(buf2hex(data));
+            try {
+                var len = 0;
+                do {
+                    len = ProcessData(obj.forwardClient);
+                    if (len > 0) { obj.forwardClient.tag.accumulator = obj.forwardClient.tag.accumulator.slice(len); }
+                    if (obj.cirastate == CIRASTATE.FAILED) {
+                        Debug("APF: in a failed state, destroying socket.");
+                        obj.forwardClient.ws.end();
+                    }
+                } while (len > 0);
+            } catch (ex) { Debug(ex); }
+        });
+
+        obj.forwardClient.ws.on('error', function (e) {
+            Debug("APF: Connection error, ending connecting.");
+            if (obj.timer != null) { clearInterval(obj.timer); obj.timer = null; }
+        });
+
+        obj.state = CIRASTATE.INITIAL;
+        if ((typeof obj.args.conntype == 'number') && (obj.args.conntype != 0)) {
+            SendJsonControl(obj.forwardClient.ws, { action: 'connType', value: obj.args.conntype });
+            if (obj.args.meiState != null) { SendJsonControl(obj.forwardClient.ws, { action: 'meiState', value: obj.args.meiState }); }
+        }
+        SendProtocolVersion(obj.forwardClient.ws, obj.args.clientuuid);
+        SendServiceRequest(obj.forwardClient.ws, '[email protected]');
+    }
+
+    obj.updateMeiState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'meiState', value: state }); }
+    obj.sendMeiDeactivationState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'deactivate', value: state }); }
+    obj.sendStartTlsHostConfigResponse = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'startTlsHostConfig', value: state }); }
+    obj.sendStopConfigurationResponse = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'stopConfiguration', value: state }); }
+
+    function SendJsonControl(socket, o) {
+        var data = JSON.stringify(o)
+        socket.write(String.fromCharCode(APFProtocol.JSON_CONTROL) + IntToStr(data.length) + data);
+        Debug("APF: Send JSON control: " + data);
+    }
+
+    function SendProtocolVersion(socket, uuid) {
+        var data = String.fromCharCode(APFProtocol.PROTOCOLVERSION) + IntToStr(1) + IntToStr(0) + IntToStr(0) + hex2rstr(strToGuid(uuid)) + binzerostring(64);
+        socket.write(data);
+        Debug("APF: Send protocol version 1 0 " + uuid);
+        obj.cirastate = CIRASTATE.PROTOCOL_VERSION_SENT;
+    }
+
+    function SendServiceRequest(socket, service) {
+        var data = String.fromCharCode(APFProtocol.SERVICE_REQUEST) + IntToStr(service.length) + service;
+        socket.write(data);
+        Debug("APF: Send service request " + service);
+        if (service == '[email protected]') {
+            obj.cirastate = CIRASTATE.AUTH_SERVICE_REQUEST_SENT;
+        } else if (service == '[email protected]') {
+            obj.cirastate = CIRASTATE.PFWD_SERVICE_REQUEST_SENT;
+        }
+    }
+
+    function SendUserAuthRequest(socket, user, pass) {
+        var service = "[email protected]";
+        var data = String.fromCharCode(APFProtocol.USERAUTH_REQUEST) + IntToStr(user.length) + user + IntToStr(service.length) + service;
+        //password auth
+        data += IntToStr(8) + 'password';
+        data += binzerostring(1) + IntToStr(pass.length) + pass;
+        socket.write(data);
+        Debug("APF: Send username password authentication to MPS");
+        obj.cirastate = CIRASTATE.AUTH_REQUEST_SENT;
+    }
+
+    function SendGlobalRequestPfwd(socket, amthostname, amtport) {
+        var tcpipfwd = 'tcpip-forward';
+        var data = String.fromCharCode(APFProtocol.GLOBAL_REQUEST) + IntToStr(tcpipfwd.length) + tcpipfwd + binzerostring(1, 1);
+        data += IntToStr(amthostname.length) + amthostname + IntToStr(amtport);
+        socket.write(data);
+        Debug("APF: Send tcpip-forward " + amthostname + ":" + amtport);
+        obj.cirastate = CIRASTATE.GLOBAL_REQUEST_SENT;
+    }
+
+    function SendKeepAliveRequest(socket) {
+        socket.write(String.fromCharCode(APFProtocol.KEEPALIVE_REQUEST) + IntToStr(255));
+        Debug("APF: Send keepalive request");
+    }
+
+    function SendKeepAliveReply(socket, cookie) {
+        socket.write(String.fromCharCode(APFProtocol.KEEPALIVE_REPLY) + IntToStr(cookie));
+        Debug("APF: Send keepalive reply");
+    }
+
+    function ProcessData(socket) {
+        var cmd = socket.tag.accumulator.charCodeAt(0);
+        var len = socket.tag.accumulator.length;
+        var data = socket.tag.accumulator;
+        if (len == 0) { return 0; }
+
+        // Respond to MPS according to obj.cirastate
+        switch (cmd) {
+            case APFProtocol.SERVICE_ACCEPT: {
+                var slen = ReadInt(data, 1), service = data.substring(5, 6 + slen);
+                Debug("APF: Service request to " + service + " accepted.");
+                if (service == '[email protected]') {
+                    if (obj.cirastate >= CIRASTATE.AUTH_SERVICE_REQUEST_SENT) {
+                        SendUserAuthRequest(socket.ws, obj.args.mpsuser, obj.args.mpspass);
+                    }
+                } else if (service == '[email protected]') {
+                    if (obj.cirastate >= CIRASTATE.PFWD_SERVICE_REQUEST_SENT) {
+                        SendGlobalRequestPfwd(socket.ws, obj.args.clientname, pfwd_ports[obj.pfwd_idx++]);
+                    }
+                }
+                return 5 + slen;
+            }
+            case APFProtocol.REQUEST_SUCCESS: {
+                if (len >= 5) {
+                    var port = ReadInt(data, 1);
+                    Debug("APF: Request to port forward " + port + " successful.");
+                    // iterate to pending port forward request
+                    if (obj.pfwd_idx < pfwd_ports.length) {
+                        SendGlobalRequestPfwd(socket.ws, obj.args.clientname, pfwd_ports[obj.pfwd_idx++]);
+                    } else {
+                        // no more port forward, now setup timer to send keep alive
+                        Debug("APF: Start keep alive for every " + obj.args.mpskeepalive + " ms.");
+                        obj.timer = setInterval(function () {
+                            SendKeepAliveRequest(obj.forwardClient.ws);
+                        }, obj.args.mpskeepalive);//
+                    }
+                    return 5;
+                }
+                Debug("APF: Request successful.");
+                return 1;
+            }
+            case APFProtocol.USERAUTH_SUCCESS: {
+                Debug("APF: User Authentication successful");
+                // Send Pfwd service request
+                SendServiceRequest(socket.ws, '[email protected]');
+                return 1;
+            }
+            case APFProtocol.USERAUTH_FAILURE: {
+                Debug("APF: User Authentication failed");
+                obj.cirastate = CIRASTATE.FAILED;
+                return 14;
+            }
+            case APFProtocol.KEEPALIVE_REQUEST: {
+                Debug("APF: Keep Alive Request with cookie: " + ReadInt(data, 1));
+                SendKeepAliveReply(socket.ws, ReadInt(data, 1));
+                return 5;
+            }
+            case APFProtocol.KEEPALIVE_REPLY: {
+                Debug("APF: Keep Alive Reply with cookie: " + ReadInt(data, 1));
+                return 5;
+            }
+            // Channel management
+            case APFProtocol.CHANNEL_OPEN: {
+                // Parse CHANNEL OPEN request
+                var p_res = parseChannelOpen(data);
+                Debug("APF: CHANNEL_OPEN request: " + JSON.stringify(p_res));
+                // Check if target port is in pfwd_ports
+                if (pfwd_ports.indexOf(p_res.target_port) >= 0) {
+                    // Connect socket to that port
+                    var chan = obj.net.createConnection({ host: obj.args.clientaddress, port: p_res.target_port }, function () {
+                        //require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "CHANNEL_OPEN-open" });
+                        // obj.downlinks[p_res.sender_chan].setEncoding('binary');//assume everything is binary, not interpreting
+                        SendChannelOpenConfirm(socket.ws, p_res);
+                    });
+
+                    // Setup flow control
+                    chan.maxInWindow = p_res.window_size; // Oddly, we are using the same window size as the other side.
+                    chan.curInWindow = 0;
+
+                    chan.on('data', function (ddata) {
+                        // Relay data to fordwardclient
+                        // TODO: Implement flow control
+                        SendChannelData(socket.ws, p_res.sender_chan, ddata);
+                    });
+
+                    chan.on('error', function (e) {
+                        //Debug("Downlink connection error: " + e);
+                        SendChannelOpenFailure(socket.ws, p_res);
+                    });
+
+                    chan.on('end', function () {
+                        var chan = obj.downlinks[p_res.sender_chan];
+                        if (chan != null) {
+                            Debug("Socket ends.");
+                            try { SendChannelClose(socket.ws, p_res.sender_chan); } catch (ex) { }
+                            delete obj.downlinks[p_res.sender_chan];
+                        }
+                    });
+
+                    obj.downlinks[p_res.sender_chan] = chan;
+                } else {
+                    // Not a supported port, fail the connection
+                    SendChannelOpenFailure(socket.ws, p_res);
+                }
+                return p_res.len;
+            }
+            case APFProtocol.CHANNEL_OPEN_CONFIRMATION: {
+                Debug("APF: CHANNEL_OPEN_CONFIRMATION");
+                return 17;
+            }
+            case APFProtocol.CHANNEL_CLOSE: {
+                var rcpt_chan = ReadInt(data, 1);
+                Debug("APF: CHANNEL_CLOSE: " + rcpt_chan);
+                try { obj.downlinks[rcpt_chan].end(); } catch (ex) { }
+                return 5;
+            }
+            case APFProtocol.CHANNEL_DATA: {
+                Debug("APF: CHANNEL_DATA: " + JSON.stringify(rstr2hex(data)));
+                var rcpt_chan = ReadInt(data, 1);
+                var chan_data_len = ReadInt(data, 5);
+                var chan_data = data.substring(9, 9 + chan_data_len);
+                var chan = obj.downlinks[rcpt_chan];
+                if (chan != null) {
+                    chan.curInWindow += chan_data_len;
+                    try {
+                        chan.write(Buffer.from(chan_data, 'binary'), function () {
+                            Debug("Write completed.");
+                            // If the incoming window is over half used, send an adjust.
+                            if (this.curInWindow > (this.maxInWindow / 2)) { SendChannelWindowAdjust(socket.ws, rcpt_chan, this.curInWindow); this.curInWindow = 0; }
+                        });
+                    } catch (ex) { Debug("Cannot forward data to downlink socket."); }
+                }
+                return 9 + chan_data_len;
+            }
+            case APFProtocol.CHANNEL_WINDOW_ADJUST: {
+                Debug("APF: CHANNEL_WINDOW_ADJUST");
+                return 9;
+            }
+            case APFProtocol.JSON_CONTROL: {
+                Debug("APF: JSON_CONTROL");
+                var len = ReadInt(data, 1);
+                if (obj.onJsonControl) { var o = null; try { o = JSON.parse(data.substring(5, 5 + len)); } catch (ex) { } if (o != null) { obj.onJsonControl(o); } }
+                return 5 + len;
+            }
+            default: {
+                Debug("CMD: " + cmd + " is not implemented.");
+                obj.cirastate = CIRASTATE.FAILED;
+                return 0;
+            }
+        }
+    }
+
+    function parseChannelOpen(data) {
+        var result = { cmd: APFProtocol.CHANNEL_OPEN };
+        var chan_type_slen = ReadInt(data, 1);
+        result.chan_type = data.substring(5, 5 + chan_type_slen);
+        result.sender_chan = ReadInt(data, 5 + chan_type_slen);
+        result.window_size = ReadInt(data, 9 + chan_type_slen);
+        var c_len = ReadInt(data, 17 + chan_type_slen);
+        result.target_address = data.substring(21 + chan_type_slen, 21 + chan_type_slen + c_len);
+        result.target_port = ReadInt(data, 21 + chan_type_slen + c_len);
+        var o_len = ReadInt(data, 25 + chan_type_slen + c_len);
+        result.origin_address = data.substring(29 + chan_type_slen + c_len, 29 + chan_type_slen + c_len + o_len);
+        result.origin_port = ReadInt(data, 29 + chan_type_slen + c_len + o_len);
+        result.len = 33 + chan_type_slen + c_len + o_len;
+        return result;
+    }
+
+    function SendChannelOpenFailure(socket, chan_data) {
+        socket.write(String.fromCharCode(APFProtocol.CHANNEL_OPEN_FAILURE) + IntToStr(chan_data.sender_chan) + IntToStr(2) + IntToStr(0) + IntToStr(0));
+        Debug("APF: Send ChannelOpenFailure");
+    }
+
+    function SendChannelOpenConfirm(socket, chan_data) {
+        socket.write(String.fromCharCode(APFProtocol.CHANNEL_OPEN_CONFIRMATION) + IntToStr(chan_data.sender_chan) + IntToStr(chan_data.sender_chan) + IntToStr(chan_data.window_size) + IntToStr(0xFFFFFFFF));
+        Debug("APF: Send ChannelOpenConfirmation");
+    }
+
+    function SendChannelWindowAdjust(socket, chan, size) {
+        socket.write(String.fromCharCode(APFProtocol.CHANNEL_WINDOW_ADJUST) + IntToStr(chan) + IntToStr(size));
+        Debug("APF: Send ChannelWindowAdjust, channel: " + chan + ", size: " + size);
+    }
+
+    function SendChannelData(socket, chan, data) {
+        socket.write(Buffer.concat([Buffer.from(String.fromCharCode(APFProtocol.CHANNEL_DATA) + IntToStr(chan) + IntToStr(data.length), 'binary'), data]));
+        Debug("APF: Send ChannelData: " + data.toString('hex'));
+    }
+
+    function SendChannelClose(socket, chan) {
+        socket.write(String.fromCharCode(APFProtocol.CHANNEL_CLOSE) + IntToStr(chan));
+        Debug("APF: Send ChannelClose ");
+    }
+
+    obj.connect = function () {
+        if (obj.forwardClient != null) {
+            try { obj.forwardClient.ws.end(); } catch (ex) { Debug(ex); }
+            //obj.forwardClient = null;
+        }
+        obj.cirastate = CIRASTATE.INITIAL;
+        obj.pfwd_idx = 0;
+
+        //obj.forwardClient = new obj.ws(obj.args.mpsurl, obj.tlsoptions);
+        //obj.forwardClient.on("open", obj.onSecureConnect);
+
+        var wsoptions = obj.http.parseUri(obj.args.mpsurl);
+        wsoptions.rejectUnauthorized = 0;
+        obj.forwardClient = obj.http.request(wsoptions);
+        obj.forwardClient.upgrade = obj.onSecureConnect;
+        obj.forwardClient.end(); // end request, trigger completion of HTTP request
+    }
+
+    obj.disconnect = function () { try { obj.forwardClient.ws.end(); } catch (ex) { Debug(ex); } }
+
+    return obj;
+}
+
+module.exports = CreateAPFClient; 

+ 610 - 0
agents/modules_meshcmd/amt-ider.js

@@ -0,0 +1,610 @@
+/** 
+* @description IDER Handling Module
+* @author Ylian Saint-Hilaire
+*/
+
+// meshservice meshcmd.js amtider --host 192.168.2.186 --pass P@ssw0rd --floppy msdos.img
+
+// Construct a Intel AMT IDER object
+module.exports = function CreateAmtRemoteIder() {
+    var obj = {};
+    obj.protocol = 3; // IDER
+    obj.bytesToAmt = 0;
+    obj.bytesFromAmt = 0;
+    obj.rx_timeout = 30000;     // Default 30000
+    obj.tx_timeout = 0;         // Default 0
+    obj.heartbeat = 20000;      // Default 20000
+    obj.version = 1;
+    obj.acc = null;
+    obj.inSequence = 0;
+    obj.outSequence = 0;
+    obj.iderinfo = null;
+    obj.enabled = false;
+    obj.iderStart = 0; // OnReboot = 0, Graceful = 1, Now = 2
+    obj.floppy = null;
+    obj.cdrom = null;
+    obj.floppyReady = false;
+    obj.cdromReady = false;
+    //obj.pingTimer = null;
+    obj.sectorStats = null;
+    obj.debug = false;
+
+    // Mode Sense
+    var IDE_ModeSence_LS120Disk_Page_Array = new Buffer([0x00, 0x26, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00]);
+    var IDE_ModeSence_3F_LS120_Array = new Buffer([0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31]);
+    var IDE_ModeSence_FloppyDisk_Page_Array = new Buffer([0x00, 0x26, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x04, 0xB0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00]);
+    var IDE_ModeSence_3F_Floppy_Array = new Buffer([0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1e, 0x04, 0xb0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xd0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31]);
+    var IDE_ModeSence_CD_1A_Array = new Buffer([0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+    //var IDE_ModeSence_CD_1B_Array = new Buffer([0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+    var IDE_ModeSence_CD_1D_Array = new Buffer([0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+    var IDE_ModeSence_CD_2A_Array = new Buffer([0x00, 0x20, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+    //var IDE_ModeSence_CD_01_Array = new Buffer([0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00]);
+    var IDE_ModeSence_3F_CD_Array = new Buffer([0x00, 0x28, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+
+    // 0x46 constant data
+    var IDE_CD_ConfigArrayHeader = new Buffer([0x00, 0x00,0x00, 0x28, 0x00, 0x00, 0x00, 0x08]);
+    var IDE_CD_ConfigArrayProfileList = new Buffer([0x00, 0x00, 0x03, 0x04, 0x00, 0x08, 0x01, 0x00]);
+    var IDE_CD_ConfigArrayCore = new Buffer([0x00, 0x01, 0x03, 0x04, 0x00, 0x00, 0x00, 0x02]);
+    var IDE_CD_Morphing = new Buffer([0x00, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00]);
+    var IDE_CD_ConfigArrayRemovable = new Buffer([0x00, 0x03, 0x03, 0x04, 0x29, 0x00, 0x00, 0x02]);
+    var IDE_CD_ConfigArrayRandom = new Buffer([0x00, 0x10, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00]);
+    var IDE_CD_Read = new Buffer([0x00, 0x1E, 0x03, 0x00]);
+    var IDE_CD_PowerManagement = new Buffer([0x01, 0x00, 0x03, 0x00]);
+    var IDE_CD_Timeout = new Buffer([0x01, 0x05, 0x03, 0x00]);
+
+    // 0x01 constant data
+    var IDE_ModeSence_FloppyError_Recovery_Array = new Buffer([0x00, 0x12, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00]);
+    var IDE_ModeSence_Ls120Error_Recovery_Array = new Buffer([0x00, 0x12, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00]);
+    var IDE_ModeSence_CDError_Recovery_Array = new Buffer([0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00]);
+
+    // CD info and performance
+    var RD_CD_DiskInfo = new Buffer([0x00, 0x20, 0x0e, 0x01, 0x01, 0x01, 0x01, 0x20, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+    var RD_CD_Performance = new Buffer([0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00]);
+
+    // Private method, called by parent when it change state
+    obj.xxStateChange = function (newstate) {
+        if (obj.debug) console.log("IDER-StateChange", newstate);
+        if (newstate == 0) { obj.Stop(); }
+        if (newstate == 3) { obj.Start(); }
+    }
+
+    obj.Start = function () {
+        if (obj.debug) { console.log("IDER-Start"); console.log(obj.floppy, obj.cdrom); }
+        obj.bytesToAmt = 0;
+        obj.bytesFromAmt = 0;
+        obj.inSequence = 0;
+        obj.outSequence = 0;
+        g_readQueue = [];
+
+        // Send first command, OPEN_SESSION
+        obj.SendCommand(0x40, Buffer.concat([ ShortToStrX(obj.rx_timeout), ShortToStrX(obj.tx_timeout), ShortToStrX(obj.heartbeat), IntToStrX(obj.version) ]));
+
+        // Send sector stats
+        if (obj.sectorStats) {
+            obj.sectorStats(0, 0, obj.floppy ? (obj.floppy.size >> 9) : 0);
+            obj.sectorStats(0, 1, obj.cdrom ? (obj.cdrom.size >> 11) : 0);
+        }
+
+        // Setup the ping timer
+        //obj.pingTimer = setInterval(function () { obj.SendCommand(0x44); }, 5000);
+    }
+
+    obj.Stop = function () {
+        if (obj.debug) console.log("IDER-Stop");
+        //if (obj.pingTimer) { clearInterval(obj.pingTimer); obj.pingTimer = null; }
+        obj.parent.Stop();
+    }
+
+    // Private method
+    obj.ProcessData = function (data) {
+        obj.bytesFromAmt += data.length;
+        if (obj.acc == null) { obj.acc = data; } else { obj.acc = Buffer.concat([obj.acc, data]); }
+        if (obj.debug) console.log('IDER-ProcessData', obj.acc.length, obj.acc.toString('hex'));
+
+        // Process as many commands as possible
+        while (obj.acc != null) {
+            var len = obj.ProcessDataEx();
+            if (len == 0) return;
+            if (obj.inSequence != ReadIntX(obj.acc, 4)) {
+                if (obj.debug) console.log('ERROR: Out of sequence', obj.inSequence, ReadIntX(obj.acc, 4));
+                obj.Stop();
+                return;
+            }
+            obj.inSequence++;
+            if (len == obj.acc.length) { obj.acc = null; } else { obj.acc = obj.acc.slice(len); }
+        }
+    }
+
+    // Private method
+    obj.SendCommand = function (cmdid, data, completed, dma) {
+        if (data == null) { data = Buffer.alloc(0); }
+        var attributes = ((cmdid > 50) && (completed == true)) ? 2 : 0;
+        if (dma) { attributes += 1; }
+        var x = Buffer.concat([Buffer([cmdid, 0, 0, attributes]), IntToStrX(obj.outSequence++), data]);
+        obj.parent.xxSend(x);
+        obj.bytesToAmt += x.length;
+        //if (cmdid != 0x4B) { console.log('IDER-SendData', x.length, x.toString('hex')); }
+    }
+
+    // CommandEndResponse (SCSI_SENSE)
+    obj.SendCommandEndResponse = function (error, sense, device, asc, asq) {
+        if (error) { obj.SendCommand(0x51, new Buffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc5, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0]), true); }
+        else { obj.SendCommand(0x51, new Buffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x87, (sense << 4), 3, 0, 0, 0, device, 0x51, sense, asc, asq]), true); }
+    }
+
+    // DataToHost (SCSI_READ)
+    obj.SendDataToHost = function (device, completed, data, dma) {
+        var dmalen = (dma) ? 0 : data.length;
+        if (completed == true) {
+            obj.SendCommand(0x54, Buffer.concat([new Buffer([0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0x85, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0, 0, 0, 0]), data ]), completed, dma);
+        } else {
+            obj.SendCommand(0x54, Buffer.concat([new Buffer([0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), data]), completed, dma);
+        }
+    }
+
+    // GetDataFromHost (SCSI_CHUNK)
+    obj.SendGetDataFromHost = function (device, chunksize) {
+        obj.SendCommand(0x52, new Buffer([0, (chunksize & 0xff), (chunksize >> 8), 0, 0xb5, 0, 0, 0, (chunksize & 0xff), (chunksize >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), false);
+    }
+
+    // DisableEnableFeatures (STATUS_DATA)
+    // If type is REGS_TOGGLE (3), 4 bytes of data must be provided.
+    obj.SendDisableEnableFeatures = function (type, data) { if (data == null) { data = ''; } obj.SendCommand(0x48, Buffer.concat([new Buffer([type]), data])); }
+
+    // Private method
+    obj.ProcessDataEx = function () {
+        if (obj.acc.length < 8) return 0;
+
+        // First 8 bytes are the header
+        // CommandID + 0x000000 + Sequence Number
+        //console.log('ProcessDataEx', obj.acc[0]);
+
+        switch(obj.acc[0]) {
+            case 0x41: // OPEN_SESSION
+                if (obj.acc.length < 30) return 0;
+                var len = obj.acc[29];
+                if (obj.acc.length < (30 + len)) return 0;
+                obj.iderinfo = {};
+                obj.iderinfo.major = obj.acc[8];
+                obj.iderinfo.minor = obj.acc[9];
+                obj.iderinfo.fwmajor = obj.acc[10];
+                obj.iderinfo.fwminor = obj.acc[11];
+                obj.iderinfo.readbfr = ReadShortX(obj.acc, 16);
+                obj.iderinfo.writebfr = ReadShortX(obj.acc, 18);
+                obj.iderinfo.proto = obj.acc[21];
+                obj.iderinfo.iana = ReadIntX(obj.acc, 25);
+                if (obj.debug) console.log(obj.iderinfo);
+
+                if (obj.iderinfo.proto != 0) {
+                    if (obj.debug) console.log("Unknown proto", obj.iderinfo.proto);
+                    obj.Stop();
+                }
+                if (obj.iderinfo.readbfr > 8192) {
+                    if (obj.debug) console.log("Illegal read buffer size", obj.iderinfo.readbfr);
+                    obj.Stop();
+                }
+                if (obj.iderinfo.writebfr > 8192) {
+                    if (obj.debug) console.log("Illegal write buffer size", obj.iderinfo.writebfr);
+                    obj.Stop();
+                }
+
+                if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
+                else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
+                else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
+                //obj.SendDisableEnableFeatures(1); // GetSupportedFeatures
+                return 30 + len;
+            case 0x43: // CLOSE
+                if (obj.debug) console.log('CLOSE');
+                obj.Stop();
+                return 8;
+            case 0x44: // KEEPALIVEPING
+                obj.SendCommand(0x45); // Send PONG back
+                return 8;
+            case 0x45: // KEEPALIVEPONG
+                if (obj.debug) console.log('PONG');
+                return 8;
+            case 0x46: // RESETOCCURED
+                if (obj.acc.length < 9) return 0;
+                var resetMask = obj.acc[8];
+                if (g_media === null) {
+                    // No operations are pending
+                    obj.SendCommand(0x47); // Send ResetOccuredResponse
+                    if (obj.debug) console.log('RESETOCCURED1', resetMask);
+                } else {
+                    // Operations are being done, sent the reset once completed.
+                    g_reset = true;
+                    if (obj.debug) console.log('RESETOCCURED2', resetMask);
+                }
+                return 9;
+            case 0x49: // STATUS_DATA - DisableEnableFeaturesReply
+                if (obj.acc.length < 13) return 0;
+                var type = obj.acc[8];
+                var value = ReadIntX(obj.acc, 9);
+                if (obj.debug) console.log('STATUS_DATA', type, value);
+                switch (type)
+                {
+                    case 1: // REGS_AVAIL
+                        if (value & 1) {
+                            if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
+                            else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
+                            else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
+                        }
+                        break;
+                    case 2: // REGS_STATUS
+                        obj.enabled = (value & 2) ? true : false;
+                        if (obj.debug) console.log("IDER Status: " + obj.enabled);
+                        break;
+                    case 3: // REGS_TOGGLE
+                        if (value != 1) {
+                            if (obj.debug) console.log("Register toggle failure");
+                        } //else { obj.SendDisableEnableFeatures(2); }
+                        break;
+                }
+                return 13;
+            case 0x4A: // ERROR OCCURED
+                if (obj.acc.length < 11) return 0;
+                if (obj.debug) console.log('IDER: ABORT', obj.acc[8]);
+                //obj.Stop();
+                return 11;
+            case 0x4B: // HEARTBEAT
+                //console.log('HEARTBEAT');
+                return 8;
+            case 0x50: // COMMAND WRITTEN
+                if (obj.acc.length < 28) return 0;
+                var device = (obj.acc[14] & 0x10) ? 0xB0 : 0xA0;
+                var deviceFlags = obj.acc[14];
+                var cdb = obj.acc.slice(16, 28);
+                var featureRegister = obj.acc[9];
+                if (obj.debug) console.log('SCSI_CMD', device, cdb.toString('hex'), featureRegister, deviceFlags);
+                handleSCSI(device, cdb, featureRegister, deviceFlags);
+                return 28;
+            case 0x53: // DATA FROM HOST
+                if (obj.acc.length < 14) return 0;
+                var len = ReadShortX(obj.acc, 9);
+                if (obj.acc.length < (14 + len)) return 0;
+                if (obj.debug) console.log('SCSI_WRITE, len = ' + (14 + len));
+                obj.SendCommand(0x51, new Buffer([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x70, 0x03, 0x00, 0x00, 0x00, 0xa0, 0x51, 0x07, 0x27, 0x00]), true);
+                return 14 + len;
+            default:
+                if (obj.debug) console.log('Unknown IDER command', obj.acc[0]);
+                obj.Stop();
+                break;
+        }
+        return 0;
+    }
+
+    function handleSCSI(dev, cdb, featureRegister, deviceFlags)
+    {
+        var lba;
+        var len;
+
+        switch(cdb[0])
+        {
+            case 0x00: // TEST_UNIT_READY:
+                if (obj.debug) console.log("SCSI: TEST_UNIT_READY", dev);
+                switch (dev) {
+                    case 0xA0: // DEV_FLOPPY
+                        if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
+                        if (obj.floppyReady == false) { obj.floppyReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready
+                        break;
+                    case 0xB0: // DEV_CDDVD
+                        if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
+                        if (obj.cdromReady == false) { obj.cdromReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready
+                        break;
+                    default:
+                        if (obj.debug) console.log("SCSI Internal error 3", dev);
+                        return -1;
+                }
+                obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); // Indicate ready
+                break;
+            case 0x08: // READ_6
+                lba = ((cdb[1] & 0x1f) << 16) + (cdb[2] << 8) + cdb[3];
+                len = cdb[4];
+                if (len == 0) { len = 256; }
+                if (obj.debug) console.log("SCSI: READ_6", dev, lba, len);
+                sendDiskData(dev, lba, len, featureRegister);
+                break;
+            case 0x0a: // WRITE_6
+                lba = ((cdb[1] & 0x1f) << 16) + (cdb[2] << 8) + cdb[3];
+                len = cdb[4];
+                if (len == 0) { len = 256; }
+                if (obj.debug) console.log("SCSI: WRITE_6", dev, lba, len);
+                obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); // Write is not supported, remote no medium.
+                return -1;
+                /*
+            case 0x15: // MODE_SELECT_6:
+                console.log("SCSI ERROR: MODE_SELECT_6", dev);
+                obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
+                return -1;
+                */
+            case 0x1a: // MODE_SENSE_6
+                if (obj.debug) console.log("SCSI: MODE_SENSE_6", dev);
+                if ((cdb[2] == 0x3f) && (cdb[3] == 0x00)) {
+                    var a = 0, b = 0;
+                    switch (dev) {
+                        case 0xA0: // DEV_FLOPPY
+                            if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
+                            a = 0x00;
+                            b = 0x80; // Read only = 0x80, Read write = 0x00
+                            break;
+                        case 0xB0: // DEV_CDDVD
+                            if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
+                            a = 0x05;
+                            b = 0x80;
+                            break;
+                        default:
+                            if (obj.debug) console.log("SCSI Internal error 6", dev);
+                            return -1;
+                    }
+                    obj.SendDataToHost(dev, true, new Buffer([0, a, b, 0]), featureRegister & 1);
+                    return;
+                }
+                obj.SendCommandEndResponse(1, 0x05, dev, 0x24, 0x00);
+                break;
+            case 0x1b: // START_STOP (Called when you eject the CDROM)
+                //var immediate = cdb[1] & 0x01;
+                //var loej = cdb[4] & 0x02;
+                //var start = cdb[4] & 0x01;
+                obj.SendCommandEndResponse(1, 0, dev);
+                break;
+            case 0x1e: // LOCK_UNLOCK - ALLOW_MEDIUM_REMOVAL
+                if (obj.debug) console.log("SCSI: ALLOW_MEDIUM_REMOVAL", dev);
+                if ((dev == 0xA0) && (obj.floppy == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
+                if ((dev == 0xB0) && (obj.cdrom == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
+                obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00);
+                break;
+            case 0x23: // READ_FORMAT_CAPACITIES (Floppy only)
+                if (obj.debug) console.log("SCSI: READ_FORMAT_CAPACITIES", dev);
+                var buflen = ReadShort(cdb, 7);
+                var mediaStatus = 0, sectors;
+                var mcSize = buflen / 8; // Capacity descriptor size is 8
+
+                switch (dev) {
+                    case 0xA0: // DEV_FLOPPY
+                        if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
+                        sectors = (obj.floppy.size >> 9) - 1;
+                        break;
+                    case 0xB0: // DEV_CDDVD
+                        if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
+                        sectors = (obj.cdrom.size >> 11) - 1; // Number 2048 byte blocks
+                        break;
+                    default:
+                        if (obj.debug) console.log("SCSI Internal error 4", dev);
+                        return -1;
+                }
+
+                obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(8), new Buffer([0x00, 0x00, 0x0b, 0x40, 0x02, 0x00, 0x02, 0x00]) ]), featureRegister & 1);
+                break;
+            case 0x25: // READ_CAPACITY
+                if (obj.debug) console.log("SCSI: READ_CAPACITY", dev);
+                var len = 0;
+                switch(dev)
+                {
+                    case 0xA0: // DEV_FLOPPY
+                        if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
+                        if (obj.floppy != null) { len = (obj.floppy.size >> 9) - 1; }
+                        if (obj.debug) console.log('DEV_FLOPPY', len); // Number 512 byte blocks
+                        break;
+                    case 0xB0: // DEV_CDDVD
+                        if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
+                        if (obj.cdrom != null) { len = (obj.cdrom.size >> 11) - 1; } // Number 2048 byte blocks
+                        if (obj.debug) console.log('DEV_CDDVD', len);
+                        break;
+                    default:
+                        if (obj.debug) console.log("SCSI Internal error 4", dev);
+                        return -1;
+                }
+                //if (dev == 0xA0) { dev = 0x00; } else { dev = 0x10; } // Weird but seems to work.
+                if (obj.debug) console.log("SCSI: READ_CAPACITY2", dev, deviceFlags);
+                obj.SendDataToHost(deviceFlags, true, Buffer.concat([IntToStr(len), new Buffer([0, 0, ((dev == 0xB0) ? 0x08 : 0x02), 0])]), featureRegister & 1);
+                break;
+            case 0x28: // READ_10
+                lba = ReadInt(cdb, 2);
+                len = ReadShort(cdb, 7);
+                if (obj.debug) console.log("SCSI: READ_10", dev, lba, len);
+                sendDiskData(dev, lba, len, featureRegister);
+                break;
+            case 0x2a: // WRITE_10 (Floppy only)
+            case 0x2e: // WRITE_AND_VERIFY (Floppy only)
+                lba = ReadInt(cdb, 2);
+                len = ReadShort(cdb, 7);
+                if (obj.debug) console.log("SCSI: WRITE_10", dev, lba, len);
+                obj.SendGetDataFromHost(dev, 512 * len); // Floppy writes only, accept sectors of 512 bytes
+                break;
+            case 0x43: // READ_TOC (CD Audio only)
+                var buflen = ReadShort(cdb, 7);
+                var msf = cdb[1] & 0x02; 
+                var format = cdb[2] & 0x07;
+                if (format == 0) { format = cdb[9] >> 6; }
+                if (obj.debug) console.log("SCSI: READ_TOC, dev=" + dev + ", buflen=" + buflen + ", msf=" + msf + ", format=" + format);
+
+                switch (dev) {
+                    case 0xA0: // DEV_FLOPPY
+                        obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00); // Not implemented
+                        return -1;
+                    case 0xB0: // DEV_CDDVD
+                        // NOP
+                        break;
+                    default:
+                        if (obj.debug) console.log("SCSI Internal error 9", dev);
+                        return -1;
+                }
+
+                if (format == 1) { obj.SendDataToHost(dev, true, new Buffer([0x00, 0x0a, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00]), featureRegister & 1); }
+                else if (format == 0) {
+                    if (msf) {
+                        obj.SendDataToHost(dev, true, new Buffer([0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x34, 0x13]), featureRegister & 1);
+                    } else {
+                        obj.SendDataToHost(dev, true, new Buffer([0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00]), featureRegister & 1);
+                    }
+                }
+                break;
+            case 0x46: // GET_CONFIGURATION
+                var sendall = (cdb[1] != 2);
+                var firstcode = ReadShort(cdb, 2);
+                var buflen = ReadShort(cdb, 7);
+
+                if (obj.debug) console.log("SCSI: GET_CONFIGURATION", dev, sendall, firstcode, buflen);
+                if (buflen == 0) { obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(0x003c), IntToStr(0x0008)]), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
+
+                // Set the header
+                var r = null;
+
+                // Add the data
+                if (firstcode == 0) { r = IDE_CD_ConfigArrayProfileList; }
+                if ((firstcode ==   0x1) || (sendall && (firstcode <   0x1))) { r = IDE_CD_ConfigArrayCore; }
+                if ((firstcode ==   0x2) || (sendall && (firstcode <   0x2))) { r = IDE_CD_Morphing; }
+                if ((firstcode ==   0x3) || (sendall && (firstcode <   0x3))) { r = IDE_CD_ConfigArrayRemovable; }
+                if ((firstcode ==  0x10) || (sendall && (firstcode <  0x10))) { r = IDE_CD_ConfigArrayRandom; }
+                if ((firstcode ==  0x1E) || (sendall && (firstcode <  0x1E))) { r = IDE_CD_Read; }
+                if ((firstcode == 0x100) || (sendall && (firstcode < 0x100))) { r = IDE_CD_PowerManagement; }
+                if ((firstcode == 0x105) || (sendall && (firstcode < 0x105))) { r = IDE_CD_Timeout; }
+
+                if (r == null) {
+                    //console.log('NOT RIGHT', sendall, firstcode, cdb[2], cdb[3]);
+                    //process.exit(0);
+                    r = Buffer.concat([IntToStr(0x0008), IntToStr(4)]);
+                } else {
+                    r = Buffer.concat([IntToStr(0x0008), IntToStr(r.length + 4), r]);
+                }
+
+                // Cut the length to buflen if needed
+                if (r.length > buflen) { r = r.slice(0, buflen); }
+
+                obj.SendDataToHost(dev, true, r, featureRegister & 1);
+                return -1;
+            case 0x4a: // GET_EV_STATUS - GET_EVENT_STATUS_NOTIFICATION
+                //var buflen = (cdb[7] << 8) + cdb[8];
+                //if (buflen == 0) { obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(0x003c), IntToStr(0x0008)]), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
+                if (obj.debug) console.log("SCSI: GET_EVENT_STATUS_NOTIFICATION", dev, cdb[1], cdb[4], cdb[9]);
+                if ((cdb[1] != 0x01) && (cdb[4] != 0x10)) {
+                    if (obj.debug) console.log('SCSI ERROR');
+                    obj.SendCommandEndResponse(1, 0x05, dev, 0x26, 0x01);
+                    break;
+                }
+                var present = 0x00;
+                if ((dev == 0xA0) && (obj.floppy != null)) { present = 0x02; }
+                else if ((dev == 0xB0) && (obj.cdrom != null)) { present = 0x02; }
+                obj.SendDataToHost(dev, true, new Buffer([0x00, present, 0x80, 0x00]), featureRegister & 1); // This is the original version, 4 bytes long
+                break;
+            case 0x4c:
+                obj.SendCommand(0x51, Buffer.concat([IntToStrX(0), IntToStrX(0), IntToStrX(0), new Buffer([0x87, 0x50, 0x03, 0x00, 0x00, 0x00, 0xb0, 0x51, 0x05, 0x20, 0x00]) ]), true);
+                break;
+            case 0x51: // READ_DISC_INFO
+                if (obj.debug) console.log("SCSI READ_DISC_INFO", dev);
+                obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // Correct
+                return -1;
+            case 0x55: // MODE_SELECT_10:
+                if (obj.debug) console.log("SCSI ERROR: MODE_SELECT_10", dev);
+                obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
+                return -1;
+            case 0x5a: // MODE_SENSE_10
+                if (obj.debug) console.log("SCSI: MODE_SENSE_10", dev, cdb[2] & 0x3f);
+                var buflen = ReadShort(cdb, 7);
+                //var pc = cdb[2] & 0xc0;
+                var r = null;
+                
+                if (buflen == 0) { obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(0x003c), IntToStr(0x0008)]), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
+
+                // 1.44 mb floppy or LS120 (sectorCount == 0x3c300)
+                var sectorCount = 0;
+                if (dev == 0xA0) {
+                    if (obj.floppy != null) { sectorCount = (obj.floppy.size >> 9); }
+                } else {
+                    if (obj.cdrom != null) { sectorCount = (obj.cdrom.size >> 11); }
+                }
+
+                switch (cdb[2] & 0x3f) {
+                    case 0x01: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_FloppyError_Recovery_Array:IDE_ModeSence_Ls120Error_Recovery_Array; } else { r = IDE_ModeSence_CDError_Recovery_Array; } break;
+                    case 0x05: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_FloppyDisk_Page_Array:IDE_ModeSence_LS120Disk_Page_Array; } break;
+                    case 0x3f: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_3F_Floppy_Array:IDE_ModeSence_3F_LS120_Array; } else { r = IDE_ModeSence_3F_CD_Array; } break;
+                    case 0x1A: if (dev == 0xB0) { r = IDE_ModeSence_CD_1A_Array; } break;
+                    case 0x1D: if (dev == 0xB0) { r = IDE_ModeSence_CD_1D_Array; } break;			
+                    case 0x2A: if (dev == 0xB0) { r = IDE_ModeSence_CD_2A_Array; } break;
+                }
+
+                if (r == null) {
+                    obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // TODO: Send proper error!!!
+                } else {
+                    // Set disk to read only (we don't support write).
+                    //ms_data[3] = ms_data[3] | 0x80;
+                    obj.SendDataToHost(dev, true, r, featureRegister & 1);
+                }
+                break;
+            case 0x51: // READ_DISK_INFORMATION
+                obj.SendDataToHost(dev, true, RD_CD_DiskInfo, featureRegister & 1);
+                break;
+            case 0xAC: // GET_PERFORMANCE
+                obj.SendDataToHost(dev, true, RD_CD_Performance, featureRegister & 1);
+                break;
+            default: // UNKNOWN COMMAND
+                if (obj.debug) console.log("IDER: Unknown SCSI command", cdb[0]);
+                obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00);
+                return -1;
+        }
+        return 0;
+    }
+
+    function sendDiskData(dev, lba, len, featureRegister) {
+        var media = null;
+        var mediaBlocks = 0;
+        if (dev == 0xA0) { media = obj.floppy; if (obj.floppy != null) { mediaBlocks = (obj.floppy.size >> 9); } }
+        if (dev == 0xB0) { media = obj.cdrom; if (obj.cdrom != null) { mediaBlocks = (obj.cdrom.size >> 11); } }
+        if ((len < 0) || (lba + len > mediaBlocks)) { obj.SendCommandEndResponse(1, 0x05, dev, 0x21, 0x00); return 0; }
+        if (len == 0) { obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); return 0; }
+        if (media != null) {
+            // Send sector stats
+            if (obj.sectorStats) { obj.sectorStats(1, (dev == 0xA0) ? 0 : 1, mediaBlocks, lba, len); }
+            if (dev == 0xA0) { lba <<= 9; len <<= 9; } else { lba <<= 11; len <<= 11; }
+            if (g_media !== null) {
+                // Queue read operation
+                g_readQueue.push({ media: media, dev: dev, lba: lba, len: len, fr: featureRegister });
+            } else {
+                // obj.iderinfo.readbfr // TODO: MaxRead
+                g_media = media;
+                g_dev = dev;
+                g_lba = lba;
+                g_len = len;
+                sendDiskDataEx(featureRegister);
+            }
+        }
+    }
+
+    var g_readQueue = [];
+    var g_reset = false;
+    var g_media = null;
+    var g_dev;
+    var g_lba;
+    var g_len;
+    function sendDiskDataEx(featureRegister) {
+        var len = g_len, lba = g_lba;
+        if (g_len > obj.iderinfo.readbfr) { len = obj.iderinfo.readbfr; }
+        g_len -= len;
+        g_lba += len;
+
+        //console.log('Read from ' + lba + ' to ' + (lba + len) + ', total of ' + len);
+
+        var result = Buffer.alloc(len);
+        fs.readSync(g_media.file, result, 0, len, lba);
+        obj.SendDataToHost(g_dev, (g_len == 0), result, featureRegister & 1);
+        if ((g_len > 0) && (g_reset == false)) {
+            sendDiskDataEx(featureRegister);
+        } else {
+            g_media = null;
+            if (g_reset) { obj.SendCommand(0x47); g_readQueue = []; g_reset = false; } // Send ResetOccuredResponse
+            else if (g_readQueue.length > 0) { var op = g_readQueue.shift(); g_media = op.media; g_dev = op.dev; g_lba = op.lba; g_len = op.len; sendDiskDataEx(op.fr); } // Un-queue read operation
+        }
+    }
+
+    return obj;
+}
+
+function ShortToStr(v) { return new Buffer([(v >> 8) & 0xFF, v & 0xFF]); }
+function ShortToStrX(v) { return new Buffer([v & 0xFF, (v >> 8) & 0xFF]); }
+function IntToStr(v) { return new Buffer([(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF]); }
+function IntToStrX(v) { return new Buffer([v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF]); }
+function ReadShort(v, p) { return (v[p] << 8) + v[p + 1]; }
+function ReadShortX(v, p) { return (v[p + 1] << 8) + v[p]; }
+function ReadInt(v, p) { return (v[p] * 0x1000000) + (v[p + 1] << 16) + (v[p + 2] << 8) + v[p + 3]; } // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
+function ReadSInt(v, p) { return (v[p] << 24) + (v[p + 1] << 16) + (v[p + 2] << 8) + v[p + 3]; }
+function ReadIntX(v, p) { return (v[p + 3] * 0x1000000) + (v[p + 2] << 16) + (v[p + 1] << 8) + v[p]; }

+ 495 - 0
agents/modules_meshcmd/amt-lme.js

@@ -0,0 +1,495 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+var MemoryStream = require('MemoryStream');
+var lme_id = 0;             // Our next channel identifier
+var lme_port_offset = 0;    // Debug: Set this to "-100" to bind to 16892 & 16893 and IN_ADDRANY. This is for LMS debugging.
+var lme_bindany = false;    // If true, bind to all network interfaces, not just loopback.
+var xmlParser = null;
+try { xmlParser = require('amt-xml'); } catch (ex) { }
+
+// Documented in: https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/HTMLDocuments/MPSDocuments/Intel%20AMT%20Port%20Forwarding%20Protocol%20Reference%20Manual.pdf
+var APF_DISCONNECT = 1;
+var APF_SERVICE_REQUEST = 5;
+var APF_SERVICE_ACCEPT = 6;
+var APF_USERAUTH_REQUEST = 50;
+var APF_USERAUTH_FAILURE = 51;
+var APF_USERAUTH_SUCCESS = 52;
+var APF_GLOBAL_REQUEST = 80;
+var APF_REQUEST_SUCCESS = 81;
+var APF_REQUEST_FAILURE = 82;
+var APF_CHANNEL_OPEN = 90;
+var APF_CHANNEL_OPEN_CONFIRMATION = 91;
+var APF_CHANNEL_OPEN_FAILURE = 92;
+var APF_CHANNEL_WINDOW_ADJUST = 93;
+var APF_CHANNEL_DATA = 94;
+var APF_CHANNEL_CLOSE = 97;
+var APF_PROTOCOLVERSION = 192;
+
+
+function lme_object() {
+    this.ourId = ++lme_id;
+    this.amtId = -1;
+    this.LME_CHANNEL_STATUS = 'LME_CS_FREE';
+    this.txWindow = 0;
+    this.rxWindow = 0;
+    this.localPort = 0;
+    this.errorCount = 0;
+}
+
+function stream_bufferedWrite() {
+    var emitterUtils = require('events').inherits(this);
+    this.buffer = [];
+    this._readCheckImmediate = undefined;
+    this._ObjectID = "bufferedWriteStream";
+    // Writable Events
+    emitterUtils.createEvent('close');
+    emitterUtils.createEvent('drain');
+    emitterUtils.createEvent('error');
+    emitterUtils.createEvent('finish');
+    emitterUtils.createEvent('pipe');
+    emitterUtils.createEvent('unpipe');
+    
+    // Readable Events
+    emitterUtils.createEvent('readable');
+    this.isEmpty = function () {
+        return (this.buffer.length == 0);
+    };
+    this.isWaiting = function () {
+        return (this._readCheckImmediate == undefined);
+    };
+    this.write = function (chunk) {
+        for (var args in arguments) { if (typeof (arguments[args]) == 'function') { this.once('drain', arguments[args]); break; } }
+        var tmp = Buffer.alloc(chunk.length);
+        chunk.copy(tmp);
+        this.buffer.push({ offset: 0, data: tmp });
+        this.emit('readable');
+        return (this.buffer.length == 0 ? true : false);
+    };
+    this.read = function () {
+        var size = arguments.length == 0 ? undefined : arguments[0];
+        var bytesRead = 0;
+        var list = [];
+        while ((size == undefined || bytesRead < size) && this.buffer.length > 0) {
+            var len = this.buffer[0].data.length - this.buffer[0].offset;
+            var offset = this.buffer[0].offset;
+            
+            if (len > (size - bytesRead)) {
+                // Only reading a subset
+                list.push(this.buffer[0].data.slice(offset, offset + size - bytesRead));
+                this.buffer[0].offset += (size - bytesRead);
+                bytesRead += (size - bytesRead);
+            } else {
+                // Reading the entire thing
+                list.push(this.buffer[0].data.slice(offset));
+                bytesRead += len;
+                this.buffer.shift();
+            }
+        }
+        this._readCheckImmediate = setImmediate(function (buffered) {
+            buffered._readCheckImmediate = undefined;
+            if (buffered.buffer.length == 0) {
+                buffered.emit('drain'); // Drained
+            } else {
+                buffered.emit('readable'); // Not drained
+            }
+        }, this);
+        return (Buffer.concat(list));
+    };
+}
+
+
+function lme_heci(options) {
+    var emitterUtils = require('events').inherits(this);
+    emitterUtils.createEvent('error');
+    emitterUtils.createEvent('connect');
+    emitterUtils.createEvent('notify');
+    emitterUtils.createEvent('bind');
+    
+    this.on('newListener', function (name, func)
+    {
+        if (name == 'connect' && this._LME._connected == true) { func.call(this); }
+        if (name == 'error' && this._LME._error !=null) { func.call(this, this._LME._error); }
+    });
+    if (options != null) {
+        if (options.debug == true) { lme_port_offset = -100; } // LMS debug mode
+        if (options.bindany == true) { lme_bindany = true; } // Bind to all ports
+    }
+
+    var heci = require('heci');
+    this.INITIAL_RXWINDOW_SIZE = 4096;
+    
+    this._ObjectID = "lme";
+    this._LME = heci.create();
+    this._LME._connected = false;
+    this._LME._error = null;
+    this._LME.descriptorMetadata = "amt-lme";
+    this._LME._binded = {};
+    this._LME.LMS = this;
+    this._LME.on('error', function (e) { this._error = e; this.LMS.emit('error', e); });
+    this._LME.on('connect', function ()
+    {
+        this._connected = true;
+        this._emitConnected = false;
+        this.on('data', function (chunk) {
+            // this = HECI
+            var cmd = chunk.readUInt8(0);
+            //console.log('LME Command ' + cmd + ', ' + chunk.length + ' byte(s).');
+            
+            switch (cmd) {
+                default:
+                    console.log('Unhandled LME Command ' + cmd + ', ' + chunk.length + ' byte(s).');
+                    break;
+                case APF_SERVICE_REQUEST:
+                    var nameLen = chunk.readUInt32BE(1);
+                    var name = chunk.slice(5, nameLen + 5);
+                    //console.log("Service Request for: " + name);
+                    if (name == '[email protected]' || name == '[email protected]') {
+                        var outBuffer = Buffer.alloc(5 + nameLen);
+                        outBuffer.writeUInt8(6, 0);
+                        outBuffer.writeUInt32BE(nameLen, 1);
+                        outBuffer.write(name.toString(), 5);
+                        this.write(outBuffer);
+                        //console.log('Answering APF_SERVICE_REQUEST');
+                    } else {
+                        //console.log('UNKNOWN APF_SERVICE_REQUEST');
+                    }
+                    break;
+                case APF_GLOBAL_REQUEST:
+                    var nameLen = chunk.readUInt32BE(1);
+                    var name = chunk.slice(5, nameLen + 5).toString();
+
+                    switch (name) {
+                        case 'tcpip-forward':
+                            var len = chunk.readUInt32BE(nameLen + 6);
+                            var port = chunk.readUInt32BE(nameLen + 10 + len);
+                            //console.log("[" + chunk.length + "/" + len + "] APF_GLOBAL_REQUEST for: " + name + " on port " + port);
+                            if (this[name] == undefined) { this[name] = {}; }
+                            if (this[name][port] != null) { // Close the existing binding
+                                for (var i in this.sockets) {
+                                    var channel = this.sockets[i];
+                                    if (channel.localPort == port) { this.sockets[i].end(); delete this.sockets[i]; } // Close this socket
+                                }
+                            }
+                            if (this[name][port] == null)
+                            {
+                                try
+                                {
+                                    // Bind a new server socket if not already present
+                                    this[name][port] = require('net').createServer();
+                                    this[name][port].descriptorMetadata = 'amt-lme (port: ' + port + ')';
+                                    this[name][port].HECI = this;
+                                    if (lme_port_offset == 0) {
+                                        if (lme_bindany) {
+                                            this[name][port].listen({ port: port }); // Bind all mode
+                                        } else {
+                                            this[name][port].listen({ port: port, host: '127.0.0.1' }); // Normal mode
+                                        }
+                                    } else {
+                                        this[name][port].listen({ port: (port + lme_port_offset) }); // Debug mode
+                                    }
+                                    this[name][port].on('connection', function (socket) {
+                                        //console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort);
+                                        this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort - lme_port_offset);
+                                    });
+                                    this._binded[port] = true;
+                                    if (!this._emitConnected)
+                                    {
+                                        this._emitConnected = true;
+                                        this.LMS.emit('error', 'APF/BIND error');
+                                    }
+                                    this.LMS.emit('bind', this._binded);
+                                } catch (ex)
+                                {
+                                    console.info1(ex, 'Port ' + port);
+                                    if(!this._emitConnected)
+                                    {
+                                        this._emitConnected = true;
+                                        this.LMS.emit('error', 'APF/BIND error');
+                                    }
+                                }
+                            }
+                            var outBuffer = Buffer.alloc(5);
+                            outBuffer.writeUInt8(81, 0);
+                            outBuffer.writeUInt32BE(port, 1);
+                            this.write(outBuffer);
+                            break;
+                        case 'cancel-tcpip-forward':
+                            var outBuffer = Buffer.alloc(1);
+                            outBuffer.writeUInt8(APF_REQUEST_SUCCESS, 0);
+                            this.write(outBuffer);
+                            break;
+                        case '[email protected]':
+                            var outBuffer = Buffer.alloc(1);
+                            outBuffer.writeUInt8(APF_REQUEST_FAILURE, 0);
+                            this.write(outBuffer);
+                            break;
+                        default:
+                            //console.log("Unknown APF_GLOBAL_REQUEST for: " + name);
+                            break;
+                    }
+                    break;
+                case APF_CHANNEL_OPEN_CONFIRMATION:
+                    var rChannel = chunk.readUInt32BE(1);
+                    var sChannel = chunk.readUInt32BE(5);
+                    var wSize = chunk.readUInt32BE(9);
+                    //console.log('rChannel/' + rChannel + ', sChannel/' + sChannel + ', wSize/' + wSize);
+                    if (this.sockets[rChannel] != undefined) {
+                        this.sockets[rChannel].lme.amtId = sChannel;
+                        this.sockets[rChannel].lme.rxWindow = wSize;
+                        this.sockets[rChannel].lme.txWindow = wSize;
+                        this.sockets[rChannel].lme.LME_CHANNEL_STATUS = 'LME_CS_CONNECTED';
+                        //console.log('LME_CS_CONNECTED');
+                        this.sockets[rChannel].bufferedStream = new stream_bufferedWrite();
+                        this.sockets[rChannel].bufferedStream.socket = this.sockets[rChannel];
+                        this.sockets[rChannel].bufferedStream.on('readable', function () {
+                            if (this.socket.lme.txWindow > 0) {
+                                var buffer = this.read(this.socket.lme.txWindow);
+                                var packet = Buffer.alloc(9 + buffer.length);
+                                packet.writeUInt8(APF_CHANNEL_DATA, 0);
+                                packet.writeUInt32BE(this.socket.lme.amtId, 1);
+                                packet.writeUInt32BE(buffer.length, 5);
+                                buffer.copy(packet, 9);
+                                this.socket.lme.txWindow -= buffer.length;
+                                this.socket.HECI.write(packet);
+                            }
+                        });
+                        this.sockets[rChannel].bufferedStream.on('drain', function () {
+                            this.socket.resume();
+                        });
+                        this.sockets[rChannel].on('data', function (chunk) {
+                            if (!this.bufferedStream.write(chunk)) { this.pause(); }
+                        });
+                        this.sockets[rChannel].on('end', function () {
+                            var outBuffer = Buffer.alloc(5);
+                            outBuffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
+                            outBuffer.writeUInt32BE(this.lme.amtId, 1);
+                            this.HECI.write(outBuffer);
+                        });
+                        this.sockets[rChannel].resume();
+                    }
+                    
+                    break;
+                case APF_PROTOCOLVERSION:
+                    var major = chunk.readUInt32BE(1);
+                    var minor = chunk.readUInt32BE(5);
+                    var reason = chunk.readUInt32BE(9);
+                    var outBuffer = Buffer.alloc(93);
+                    outBuffer.writeUInt8(192, 0);
+                    outBuffer.writeUInt32BE(1, 1);
+                    outBuffer.writeUInt32BE(0, 5);
+                    outBuffer.writeUInt32BE(reason, 9);
+                    //console.log('Answering PROTOCOL_VERSION');
+                    this.write(outBuffer);
+                    break;
+                case APF_CHANNEL_WINDOW_ADJUST:
+                    var rChannelId = chunk.readUInt32BE(1);
+                    var bytesToAdd = chunk.readUInt32BE(5);
+                    if (this.sockets[rChannelId] != undefined) {
+                        this.sockets[rChannelId].lme.txWindow += bytesToAdd;
+                        if (!this.sockets[rChannelId].bufferedStream.isEmpty() && this.sockets[rChannelId].bufferedStream.isWaiting()) {
+                            this.sockets[rChannelId].bufferedStream.emit('readable');
+                        }
+                    } else {
+                        console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_WINDOW_ADJUST');
+                    }
+                    break;
+                case APF_CHANNEL_DATA:
+                    var rChannelId = chunk.readUInt32BE(1);
+                    var dataLen = chunk.readUInt32BE(5);
+                    var data = chunk.slice(9, 9 + dataLen);
+                    if ((this.sockets != null) && (this.sockets[rChannelId] != undefined)) {
+                        this.sockets[rChannelId].pendingBytes.push(data.length);
+                        this.sockets[rChannelId].write(data, function () {
+                            var written = this.pendingBytes.shift();
+                            //console.log('adjust', this.lme.amtId, written);
+                            var outBuffer = Buffer.alloc(9);
+                            outBuffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0);
+                            outBuffer.writeUInt32BE(this.lme.amtId, 1);
+                            outBuffer.writeUInt32BE(written, 5);
+                            this.HECI.write(outBuffer);
+                        });
+                    } else if ((this.insockets != null) && (this.insockets[rChannelId] != undefined)) {
+                        var channel = this.insockets[rChannelId];
+                        if (channel.data == null) { channel.data = data.toString(); } else { channel.data += data.toString(); }
+                        channel.rxWindow += dataLen;
+                        //console.log('IN DATA', channel.rxWindow, channel.data.length, dataLen, channel.amtId, data.toString());
+                        var httpData = parseHttp(channel.data);
+                        if ((httpData != null) || (channel.data.length >= 8000)) {
+                            // Parse the WSMAN
+                            var notify = null;
+                            if (xmlParser != null) { try { notify = xmlParser.ParseWsman(httpData); } catch (e) { } }
+
+                            // Event the http data
+                            if (notify != null) { this.LMS.emit('notify', notify, channel.options, _lmsNotifyToCode(notify)); }
+
+                            // Send channel close
+                            var buffer = Buffer.alloc(5);
+                            buffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
+                            buffer.writeUInt32BE(amtId, 1);
+                            this.write(buffer);
+                        } else {
+                            if (channel.rxWindow > 6000) {
+                                // Send window adjust
+                                var buffer = Buffer.alloc(9);
+                                buffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0);
+                                buffer.writeUInt32BE(channel.amtId, 1);
+                                buffer.writeUInt32BE(channel.rxWindow, 5);
+                                this.write(buffer);
+                                channel.rxWindow = 0;
+                            }
+                        }
+                    } else {
+                        console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_DATA');
+                    }
+                    break;
+                case APF_CHANNEL_OPEN_FAILURE:
+                    var rChannelId = chunk.readUInt32BE(1);
+                    var reasonCode = chunk.readUInt32BE(5);
+                    if ((this.sockets != null) && (this.sockets[rChannelId] != undefined)) {
+                        this.sockets[rChannelId].end();
+                        delete this.sockets[rChannelId];
+                    } else if ((this.insockets != null) && (this.insockets[rChannelId] != undefined)) {
+                        delete this.insockets[rChannelId];
+                    } else {
+                        console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_OPEN_FAILURE');
+                    }
+                    break;
+                case APF_CHANNEL_CLOSE:
+                    var rChannelId = chunk.readUInt32BE(1);
+                    if ((this.sockets != null) && (this.sockets[rChannelId] != undefined)) {
+                        this.sockets[rChannelId].end();
+                        var amtId = this.sockets[rChannelId].lme.amtId;
+                        var buffer = Buffer.alloc(5);
+                        delete this.sockets[rChannelId];
+                        
+                        buffer.writeUInt8(APF_CHANNEL_CLOSE, 0); // ????????????????????????????
+                        buffer.writeUInt32BE(amtId, 1);
+                        this.write(buffer);
+                    } else if ((this.insockets != null) && (this.insockets[rChannelId] != undefined)) {
+                        delete this.insockets[rChannelId];
+                        // Should I send a close back????
+                    } else {
+                        console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_CLOSE');
+                    }
+                    break;
+                case APF_CHANNEL_OPEN:
+                    var nameLen = chunk.readUInt32BE(1);
+                    var name = chunk.slice(5, nameLen + 5).toString();
+                    var channelSender = chunk.readUInt32BE(nameLen + 5);
+                    var initialWindowSize = chunk.readUInt32BE(nameLen + 9);
+                    var hostToConnectLen = chunk.readUInt32BE(nameLen + 17);
+                    var hostToConnect = chunk.slice(nameLen + 21, nameLen + 21 + hostToConnectLen).toString();
+                    var portToConnect = chunk.readUInt32BE(nameLen + 21 + hostToConnectLen);
+                    var originatorIpLen = chunk.readUInt32BE(nameLen + 25 + hostToConnectLen);
+                    var originatorIp = chunk.slice(nameLen + 29 + hostToConnectLen, nameLen + 29 + hostToConnectLen + originatorIpLen).toString();
+                    var originatorPort = chunk.readUInt32BE(nameLen + 29 + hostToConnectLen + originatorIpLen);
+                    //console.log('APF_CHANNEL_OPEN', name, channelSender, initialWindowSize, 'From: ' + originatorIp + ':' + originatorPort, 'To: ' + hostToConnect + ':' + portToConnect);
+
+                    if (this.insockets == null) { this.insockets = {}; }
+                    var ourId = ++lme_id;
+                    var insocket = new lme_object();
+                    insocket.ourId = ourId;
+                    insocket.amtId = channelSender;
+                    insocket.txWindow = initialWindowSize;
+                    insocket.rxWindow = 0;
+                    insocket.options = { target: hostToConnect, targetPort: portToConnect, source: originatorIp, sourcePort: originatorPort };
+                    this.insockets[ourId] = insocket;
+
+                    var buffer = Buffer.alloc(17);
+                    buffer.writeUInt8(APF_CHANNEL_OPEN_CONFIRMATION, 0);
+                    buffer.writeUInt32BE(channelSender, 1);     // Intel AMT sender channel
+                    buffer.writeUInt32BE(ourId, 5);             // Our receiver channel id
+                    buffer.writeUInt32BE(4000, 9);              // Initial Window Size
+                    buffer.writeUInt32BE(0xFFFFFFFF, 13);       // Reserved
+                    this.write(buffer);
+
+                    /*
+                    var buffer = Buffer.alloc(17);
+                    buffer.writeUInt8(APF_CHANNEL_OPEN_FAILURE, 0);
+                    buffer.writeUInt32BE(channelSender, 1);     // Intel AMT sender channel
+                    buffer.writeUInt32BE(2, 5);                 // Reason code
+                    buffer.writeUInt32BE(0, 9);                 // Reserved
+                    buffer.writeUInt32BE(0, 13);                // Reserved
+                    this.write(buffer);
+                    console.log('Sent APF_CHANNEL_OPEN_FAILURE', channelSender);
+                    */
+
+                    break;
+            }
+        });
+        //
+        // Due to a change in behavior with AMT/11 (and possibly earlier), we are not going to emit 'connect' here, until
+        // we can verify that the first APF/Channel can be bound. Older AMT, like AMT/7 only allowed a single LME connection, so we
+        // used to emit connect here. However, newer AMT's will allow more than 1 LME connection, which will result in APF/Bind failure
+        //
+        //this.LMS.emit('connect');
+        this.resume();
+
+    });
+    
+    this.bindDuplexStream = function (duplexStream, remoteFamily, localPort) {
+        var socket = duplexStream;
+        //console.log('New [' + remoteFamily + '] Virtual Connection/' + socket.localPort);
+        socket.pendingBytes = [];
+        socket.HECI = this._LME;
+        socket.LMS = this;
+        socket.lme = new lme_object();
+        socket.lme.Socket = socket;
+        socket.localPort = localPort;
+        var buffer = new MemoryStream();
+        buffer.writeUInt8(0x5A);
+        buffer.writeUInt32BE(15);
+        buffer.write('forwarded-tcpip');
+        buffer.writeUInt32BE(socket.lme.ourId);
+        buffer.writeUInt32BE(this.INITIAL_RXWINDOW_SIZE);
+        buffer.writeUInt32BE(0xFFFFFFFF);
+        for (var i = 0; i < 2; ++i) {
+            if (remoteFamily == 'IPv6') {
+                buffer.writeUInt32BE(3);
+                buffer.write('::1');
+            } else {
+                buffer.writeUInt32BE(9);
+                buffer.write('127.0.0.1');
+            }
+            buffer.writeUInt32BE(localPort);
+        }
+        this._LME.write(buffer.buffer);
+        if (this._LME.sockets == undefined) { this._LME.sockets = {}; }
+        this._LME.sockets[socket.lme.ourId] = socket;
+        socket.pause();
+    };
+    
+    this._LME.connect(heci.GUIDS.LME, { noPipeline: 0 });
+}
+
+function parseHttp(httpData) {
+    var i = httpData.indexOf('\r\n\r\n');
+    if ((i == -1) || (httpData.length < (i + 2))) { return null; }
+    var headers = require('http-headers')(httpData.substring(0, i), true);
+    var contentLength = parseInt(headers['content-length']);
+    if (httpData.length >= contentLength + i + 4) { return httpData.substring(i + 4, i + 4 + contentLength); }
+    return null;
+}
+
+function _lmsNotifyToCode(notify) {
+    if ((notify == null) || (notify.Body == null) || (notify.Body.MessageID == null)) return null;
+    var msgid = notify.Body.MessageID;
+    try { msgid += '-' + notify.Body.MessageArguments[0]; } catch (e) { }
+    return msgid;
+}
+
+module.exports = lme_heci;

+ 499 - 0
agents/modules_meshcmd/amt-mei.js

@@ -0,0 +1,499 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+var Q = require('queue');
+var g_internal = null;
+
+function retry_pthi_later()
+{
+    if (++g_internal.errorCount < 20)
+    {
+        g_internal.timeout = setTimeout(function (p)
+        {
+            p.connect(require('heci').GUIDS.AMT, { noPipeline: 1 });
+        }, 250, this);
+    }
+    else
+    {
+        this.Parent.emit('error', 'PTHI Connection could not be established'); 
+    }
+}
+
+function amt_heci()
+{
+    var emitterUtils = require('events').inherits(this);
+    emitterUtils.createEvent('error');
+
+    var heci = require('heci');
+    var sendConsole = function (msg) { try { require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": msg }); } catch (ex) { } }
+
+    this._ObjectID = "pthi";
+    var that = this;
+    if (g_internal == null)
+    {
+        g_internal = { _rq: new Q(), _amt: null, errorCount: 0 };
+        g_internal._setupPTHI = function _g_setupPTHI()
+        {
+            console.info1('setupPTHI()');
+            this._amt = heci.create();
+            this._amt.descriptorMetadata = "amt-pthi";
+            this._amt.BiosVersionLen = 65;
+            this._amt.UnicodeStringLen = 20;
+            this._amt.Parent = that;
+
+            this._amt.on('error', function _amtOnError(e)
+            {
+                console.info1('PTHIError: ' + e);
+                if (g_internal._rq.isEmpty())
+                {
+                    console.info1(' Queue is empty');
+                    this.Parent.emit('error', e); // No pending requests, so propagate the error up
+                }
+                else
+                {
+                    console.info1(' Queue is NOT empty');
+
+                    // Try again
+                    retry_pthi_later.call(this);
+                }
+            });
+            this._amt.on('connect', function _amtOnConnect()
+            {
+                g_internal.errorCount = 0;
+                this.on('data', function _amtOnData(chunk)
+                {
+                    //console.log("Received: " + chunk.length + " bytes");
+                    var header = this.Parent.getCommand(chunk);
+                    console.info1("CMD = " + header.Command + " (Status: " + header.Status + ") Response = " + header.IsResponse);
+
+                    var user = g_internal._rq.deQueue();
+                    var params = user.optional;
+                    var callback = user.func;
+
+                    params.unshift(header);
+                    callback.apply(this.Parent, params);
+
+                    if (g_internal._rq.isEmpty())
+                    {
+                        console.info1('No more requests, disconnecting');
+
+                        // No More Requests, we can close PTHI
+                        g_internal._amt.disconnect();
+                        g_internal._amt = null;
+                    }
+                    else
+                    {
+                        // Send the next request
+                        console.info1('Sending Next Request');
+                        this.write(g_internal._rq.peekQueue().send);
+                    }
+                });
+
+                // Start sending requests
+                this.write(g_internal._rq.peekQueue().send);
+            });
+
+
+
+        };
+    }
+
+    
+    function trim(x) { var y = x.indexOf('\0'); if (y >= 0) { return x.substring(0, y); } else { return x; } }
+    this.getCommand = function getCommand(chunk) {
+        var command = chunk.length == 0 ? (g_internal._rq.peekQueue().cmd | 0x800000) : chunk.readUInt32LE(4);
+        var ret = { IsResponse: (command & 0x800000) == 0x800000 ? true : false, Command: (command & 0x7FFFFF), Status: chunk.length != 0 ? chunk.readUInt32LE(12) : -1, Data: chunk.length != 0 ? chunk.slice(16) : null };
+        return (ret);
+    };
+
+    this.sendCommand = function sendCommand()
+    {
+        if (arguments.length < 3 || typeof (arguments[0]) != 'number' || typeof (arguments[1]) != 'object' || typeof (arguments[2]) != 'function') { throw ('invalid parameters'); }
+        var args = [];
+        for (var i = 3; i < arguments.length; ++i) { args.push(arguments[i]); }
+
+        console.info1('sendCommand(' + arguments[0] + ')', this._hashCode());
+
+        var header = Buffer.from('010100000000000000000000', 'hex');
+        header.writeUInt32LE(arguments[0] | 0x04000000, 4);
+        header.writeUInt32LE(arguments[1] == null ? 0 : arguments[1].length, 8);
+
+        g_internal._rq.enQueue({ cmd: arguments[0], func: arguments[2], optional: args, send: (arguments[1] == null ? header : Buffer.concat([header, arguments[1]])) });
+        if (!g_internal._amt)
+        {
+            g_internal._setupPTHI();
+            g_internal._amt.connect(heci.GUIDS.AMT, { noPipeline: 1 });
+        }
+    }
+
+    this.getVersion = function getVersion(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(26, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var i, CodeVersion = header.Data, val = { BiosVersion: CodeVersion.slice(0, g_internal._amt.BiosVersionLen).toString(), Versions: [] }, v = CodeVersion.slice(g_internal._amt.BiosVersionLen + 4);
+                for (i = 0; i < CodeVersion.readUInt32LE(g_internal._amt.BiosVersionLen) ; ++i)
+                {
+                    val.Versions[i] = { Description: v.slice(2, v.readUInt16LE(0) + 2).toString(), Version: v.slice(4 + g_internal._amt.UnicodeStringLen, 4 + g_internal._amt.UnicodeStringLen + v.readUInt16LE(2 + g_internal._amt.UnicodeStringLen)).toString() };
+                    v = v.slice(4 + (2 * g_internal._amt.UnicodeStringLen));
+                }
+                if (val.BiosVersion.indexOf('\0') > 0) { val.BiosVersion = val.BiosVersion.substring(0, val.BiosVersion.indexOf('\0')); }
+                opt.unshift(val);
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+
+    // Fill the left with zeros until the string is of a given length
+    function zeroLeftPad(str, len) {
+        if ((len == null) && (typeof (len) != 'number')) { return null; }
+        if (str == null) str = ''; // If null, this is to generate zero leftpad string
+        var zlp = '';
+        for (var i = 0; i < len - str.length; i++) { zlp += '0'; }
+        return zlp + str;
+    }
+
+    this.getUuid = function getUuid(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x5c, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var result = {};
+                result.uuid = [zeroLeftPad(header.Data.readUInt32LE(0).toString(16), 8),
+                    zeroLeftPad(header.Data.readUInt16LE(4).toString(16), 4),
+                    zeroLeftPad(header.Data.readUInt16LE(6).toString(16), 4),
+                    zeroLeftPad(header.Data.readUInt16BE(8).toString(16), 4),
+                    zeroLeftPad(header.Data.slice(10).toString('hex').toLowerCase(), 12)].join('-');
+                opt.unshift(result);
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+
+    this.getProvisioningState = function getProvisioningState(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(17, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var result = {};
+                result.state = header.Data.readUInt32LE(0);
+                if (result.state < 3) { result.stateStr = ["PRE", "IN", "POST"][result.state]; }
+                opt.unshift(result);
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getProvisioningMode = function getProvisioningMode(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(8, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var result = {};
+                result.mode = header.Data.readUInt32LE(0);
+                if (result.mode < 4) { result.modeStr = ["NONE", "ENTERPRISE", "SMALL_BUSINESS", "REMOTE_ASSISTANCE"][result.mode]; }
+                result.legacy = header.Data.readUInt32LE(4) == 0 ? false : true;
+                opt.unshift(result);
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getEHBCState = function getEHBCState(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(132, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                opt.unshift({ EHBC: header.Data.readUInt32LE(0) != 0 });
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getControlMode = function getControlMode(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(107, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var result = {};
+                result.controlMode = header.Data.readUInt32LE(0);
+                if (result.controlMode < 3) { result.controlModeStr = ["NONE_RPAT", "CLIENT", "ADMIN", "REMOTE_ASSISTANCE"][result.controlMode]; }
+                opt.unshift(result);
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getMACAddresses = function getMACAddresses(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(37, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                opt.unshift({ DedicatedMAC: header.Data.slice(0, 6).toString('hex:'), HostMAC: header.Data.slice(6, 12).toString('hex:') });
+            } else { opt.unshift({ DedicatedMAC: null, HostMAC: null }); }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getDnsSuffix = function getDnsSuffix(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(54, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var resultLen = header.Data.readUInt16LE(0);
+                if (resultLen > 0) { opt.unshift(header.Data.slice(2, 2 + resultLen).toString()); } else { opt.unshift(null); }
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getHashHandles = function getHashHandles(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x2C, null, function (header, fn, opt) {
+            var result = [];
+            if (header.Status == 0) {
+                var resultLen = header.Data.readUInt32LE(0);
+                for (var i = 0; i < resultLen; ++i) {
+                    result.push(header.Data.readUInt32LE(4 + (4 * i)));
+                }
+            }
+            opt.unshift(result);
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getCertHashEntry = function getCertHashEntry(handle, callback) {
+        var optional = [];
+        for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
+
+        var data = Buffer.alloc(4);
+        data.writeUInt32LE(handle, 0);
+
+        this.sendCommand(0x2D, data, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var result = {};
+                result.isDefault = header.Data.readUInt32LE(0);
+                result.isActive = header.Data.readUInt32LE(4);
+                result.hashAlgorithm = header.Data.readUInt8(72);
+                if (result.hashAlgorithm < 4) {
+                    result.hashAlgorithmStr = ["MD5", "SHA1", "SHA256", "SHA512"][result.hashAlgorithm];
+                    result.hashAlgorithmSize = [16, 20, 32, 64][result.hashAlgorithm];
+                    result.certificateHash = header.Data.slice(8, 8 + result.hashAlgorithmSize).toString('hex');
+                }
+                result.name = header.Data.slice(73 + 2, 73 + 2 + header.Data.readUInt16LE(73)).toString();
+                opt.unshift(result);
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getCertHashEntries = function getCertHashEntries(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+
+        this.getHashHandles(function (handles, fn, opt) {
+            var entries = [];
+            this.getCertHashEntry(handles.shift(), this._getHashEntrySink, fn, opt, entries, handles);
+        }, callback, optional);
+    };
+
+    this._getHashEntrySink = function _getHashEntrySink(result, fn, opt, entries, handles) {
+        entries.push(result);
+        if (handles.length > 0) {
+            this.getCertHashEntry(handles.shift(), this._getHashEntrySink, fn, opt, entries, handles);
+        } else {
+            opt.unshift(entries);
+            fn.apply(this, opt);
+        }
+    }
+    this.getLocalSystemAccount = function getLocalSystemAccount(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(103, Buffer.alloc(40), function (header, fn, opt) {
+            if (header.Status == 0 && header.Data.length == 68) {
+                opt.unshift({ user: trim(header.Data.slice(0, 33).toString()), pass: trim(header.Data.slice(33, 67).toString()), raw: header.Data });
+            }
+            else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    }
+    this.getLanInterfaceSettings = function getLanInterfaceSettings(index, callback) {
+        var optional = [];
+        for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        var ifx = Buffer.alloc(4);
+        ifx.writeUInt32LE(index);
+        this.sendCommand(0x48, ifx, function onGetLanInterfaceSettings(header, fn, opt) {
+            if (header.Status == 0) {
+                var info = {};
+                info.enabled = header.Data.readUInt32LE(0);
+                info.dhcpEnabled = header.Data.readUInt32LE(8);
+                switch (header.Data[12]) {
+                    case 1:
+                        info.dhcpMode = 'ACTIVE'
+                        break;
+                    case 2:
+                        info.dhcpMode = 'PASSIVE'
+                        break;
+                    default:
+                        info.dhcpMode = 'UNKNOWN';
+                        break;
+                }
+                info.mac = header.Data.slice(14).toString('hex:');
+
+                var addr = header.Data.readUInt32LE(4);
+                info.address = ((addr >> 24) & 255) + '.' + ((addr >> 16) & 255) + '.' + ((addr >> 8) & 255) + '.' + (addr & 255);
+                opt.unshift(info);
+                fn.apply(this, opt);
+            }
+            else {
+                opt.unshift(null);
+                fn.apply(this, opt);
+            }
+        }, callback, optional);
+
+    };
+    this.unprovision = function unprovision(mode, callback) {
+        var optional = [];
+        for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        var data = Buffer.alloc(4);
+        data.writeUInt32LE(mode, 0);
+        this.sendCommand(16, data, function (header, fn, opt) {
+            opt.unshift(header.Status);
+            fn.apply(this, opt);
+        }, callback, optional);
+    }
+    this.startConfiguration = function startConfiguration(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x29, null, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
+    }
+    this.stopConfiguration = function stopConfiguration(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x5E, null, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
+    }
+    this.openUserInitiatedConnection = function openUserInitiatedConnection(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x44, null, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
+    }
+    this.closeUserInitiatedConnection = function closeUnserInitiatedConnected(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x45, null, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
+    }
+    this.getRemoteAccessConnectionStatus = function getRemoteAccessConnectionStatus(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x46, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var hostname = header.Data.slice(14, header.Data.readUInt16LE(12) + 14).toString()
+                opt.unshift({ status: header.Status, networkStatus: header.Data.readUInt32LE(0), remoteAccessStatus: header.Data.readUInt32LE(4), remoteAccessTrigger: header.Data.readUInt32LE(8), mpsHostname: hostname, raw: header.Data });
+            } else {
+                opt.unshift({ status: header.Status });
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    }
+    this.getProtocolVersion = function getProtocolVersion(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { opt.push(arguments[i]); }
+
+        if (!this._tmpSession) { this._tmpSession = heci.create(); this._tmpSession.parent = this; }
+        this._tmpSession.doIoctl(heci.IOCTL.HECI_VERSION, Buffer.alloc(5), Buffer.alloc(5), function (status, buffer, self, fn, opt) {
+            if (status == 0) {
+                var result = buffer.readUInt8(0).toString() + '.' + buffer.readUInt8(1).toString() + '.' + buffer.readUInt8(2).toString() + '.' + buffer.readUInt16BE(3).toString();
+                opt.unshift(result);
+                fn.apply(self, opt);
+            }
+            else {
+                opt.unshift(null);
+                fn.apply(self, opt);
+            }
+
+        }, this, callback, optional);
+    }
+    this.startConfigurationHBased = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
+        if ((certHash == null) || ((certHash.length != 32) && (certHash.length != 48))) { func({ status: -101 }); }
+        this.stopConfiguration(function (status) {
+            if (status == 0) {
+                // We stopped the configuration, wait 20 seconds before starting up again.
+                var f = function tf() { delete tf.parent.xtimeout; tf.parent.startConfigurationHBasedEx(certHash, hostVpn, dnsSuffixList, func); }
+                f.parent = this;
+                this.xtimeout = setTimeout(f, 20000);
+            } else {
+                // We are not in the connect mode, this is good, start configuration right away.
+                this.startConfigurationHBasedEx(certHash, hostVpn, dnsSuffixList, func);
+            }
+        })
+    }
+    this.startConfigurationHBasedEx = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
+        var optional = [];
+        for (var i = 4; i < arguments.length; ++i) { optional.push(arguments[i]); }
+
+        // Format the command
+        var data = Buffer.alloc(1 + 64 + 4 + 4 + ((dnsSuffixList != null) ? 320 : 0));
+        data[0] = (certHash.length == 48) ? 3 : 2 // Write certificate hash type: SHA256 = 2, SHA384 = 3
+        certHash.copy(data, 1); // Write the hash
+        data.writeUInt32LE(hostVpn ? 1 : 0, 65); // Write is HostVPN is enabled
+        if (dnsSuffixList != null) {
+            data.writeUInt32LE(dnsSuffixList.length, 69); // Write the number of DNS Suffix, from 0 to 4
+            var ptr = 73;
+            for (var i = 0; i < dnsSuffixList.length; i++) { ptr += data.write(dnsSuffixList[i], ptr) + 1; } // Write up to 4 DNS Suffix with null seperation.
+        }
+
+        // Send the command
+        this.sendCommand(139, data, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var amtHash = null;
+                if (header.Data[0] == 2) { amtHash = header.Data.slice(1, 33); } // SHA256
+                if (header.Data[0] == 3) { amtHash = header.Data.slice(1, 49); } // SHA384
+                opt.unshift({ status: header.Status, hash: amtHash.toString('hex') });
+            } else {
+                opt.unshift({ status: header.Status });
+            }
+            fn.apply(this, opt);
+        }, func, optional);
+    }
+}
+
+module.exports = amt_heci;
+
+
+/*
+AMT_STATUS_SUCCESS = 0,
+AMT_STATUS_INTERNAL_ERROR = 1,
+AMT_STATUS_INVALID_AMT_MODE = 3,
+AMT_STATUS_INVALID_MESSAGE_LENGTH = 4,
+AMT_STATUS_MAX_LIMIT_REACHED = 23,
+AMT_STATUS_INVALID_PARAMETER = 36,
+AMT_STATUS_RNG_GENERATION_IN_PROGRESS = 47,
+AMT_STATUS_RNG_NOT_READY = 48,
+AMT_STATUS_CERTIFICATE_NOT_READY = 49,
+AMT_STATUS_INVALID_HANDLE = 2053
+AMT_STATUS_NOT_FOUND = 2068,
+*/

+ 309 - 0
agents/modules_meshcmd/amt-redir-duk.js

@@ -0,0 +1,309 @@
+/** 
+* @description Intel AMT Redirection Transport Module - using Node
+* @author Ylian Saint-Hilaire
+* @version v0.0.1f
+*/
+
+// Construct a MeshServer object
+module.exports = function CreateAmtRedirect(module) {
+    var obj = {};
+    obj.m = module; // This is the inner module (Terminal or Desktop)
+    module.parent = obj;
+    obj.State = 0;
+    obj.net = require('net');
+    obj.tls = require('tls');
+    obj.socket = null;
+    obj.host = null;
+    obj.port = 0;
+    obj.user = null;
+    obj.pass = null;
+    obj.connectstate = 0;
+    obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER
+    obj.xtlsoptions = null;
+
+    obj.amtaccumulator = Buffer.alloc(0);
+    obj.amtsequence = 1;
+    obj.amtkeepalivetimer = null;
+    obj.authuri = '/RedirectionService';
+    obj.digestRealmMatch = null;
+
+    obj.onStateChanged = null;
+
+    // Private method
+    obj.Debug = function (msg) { console.log(msg); }
+    var urlvars = null;
+
+    obj.Start = function (host, port, user, pass, tls, tlsFingerprint, tlsoptions) {
+        obj.host = host;
+        obj.port = port;
+        obj.user = user;
+        obj.pass = pass;
+        obj.xtls = tls;
+        obj.xtlsoptions = tlsoptions;
+        obj.xtlsFingerprint = tlsFingerprint;
+        obj.connectstate = 0;
+
+        if (tls == true) {
+            obj.socket = obj.tls.connect({ host: host, port: port, rejectUnauthorized: false, checkServerIdentity: obj.onCheckServerIdentity }, obj.xxOnSocketConnected);
+        } else {
+            obj.socket = obj.net.createConnection({ host: host, port: port }, obj.xxOnSocketConnected);
+        }
+        obj.socket.on('data', obj.xxOnSocketData);
+        obj.socket.on('close', obj.xxOnSocketClosed);
+        obj.socket.on('error', obj.xxOnSocketClosed);
+        obj.xxStateChange(1);
+    }
+
+    // Get the certificate of Intel AMT
+    //obj.getPeerCertificate = function () { if (obj.xtls == true) { return obj.socket.getPeerCertificate(); } return null; }
+
+    obj.onCheckServerIdentity = function (cert) {
+        var f = cert[0].fingerprint.split(':').join('').toLowerCase();
+        if ((obj.xtlsFingerprint != null) && (obj.xtlsFingerprint != f)) {
+            console.log('Invalid TLS Cert, SHA384: ' + f);
+            process.exit(2);
+            return;
+        } else {
+            if (obj.xtlsFingerprint == null) {
+                obj.xtlsFingerprint = f;
+                console.log('TLS Cert SHA384: ' + f);
+            }
+        }
+    }
+
+    obj.xxOnSocketConnected = function () {
+        if (obj.socket == null) return;
+        /*
+        if (obj.xtls == true) {
+            obj.xtlsCertificate = obj.socket.getPeerCertificate();
+            if ((obj.xtlsFingerprint != 0) && (obj.xtlsCertificate.fingerprint.split(':').join('').toLowerCase() != obj.xtlsFingerprint)) { obj.Stop(); return; }
+        }
+        */
+
+        if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CONNECTED'); }
+        //obj.Debug("Socket Connected");
+        obj.xxStateChange(2);
+        if (obj.protocol == 1) obj.xxSend(obj.RedirectStartSol); // TODO: Put these strings in higher level module to tighten code
+        else if (obj.protocol == 2) obj.xxSend(obj.RedirectStartKvm); // Don't need these is the feature if not compiled-in.
+        else if (obj.protocol == 3) obj.xxSend(obj.RedirectStartIder);
+    }
+   
+    obj.xxOnSocketData = function (data) {
+        if (!data || obj.connectstate == -1) return;
+        if (urlvars && urlvars['redirtrace']) { console.log('REDIR-RECV(' + data.length + '): ' + data.toString('hex')); }
+        //obj.Debug("Recv(" + data.length + "): " + rstr2hex(data));
+        if ((obj.protocol == 2 || obj.protocol == 3) && obj.connectstate == 1) { return obj.m.ProcessData(data); } // KVM or IDER traffic, forward it directly.
+        obj.amtaccumulator = Buffer.concat([obj.amtaccumulator, data]);
+        //obj.Debug("Recv(" + obj.amtaccumulator.length + "): " + obj.amtaccumulator.toString('hex'));
+
+        while (obj.amtaccumulator.length > 0) {
+            var cmdsize = 0;
+            //console.log('CMD: ' + obj.amtaccumulator[0]);
+            switch (obj.amtaccumulator[0]) {
+                case 0x11: // StartRedirectionSessionReply (17)
+                    if (obj.amtaccumulator.length < 4) return;
+                    var statuscode = obj.amtaccumulator[1];
+                    switch (statuscode) {
+                        case 0: // STATUS_SUCCESS
+                            if (obj.amtaccumulator.length < 13) return;
+                            var oemlen = obj.amtaccumulator[12];
+                            if (obj.amtaccumulator.length < 13 + oemlen) return;
+                            obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); // Query authentication support
+                            cmdsize = (13 + oemlen);
+                            break;
+                        default:
+                            obj.Stop();
+                            break;
+                    }
+                    break;
+                case 0x14: // AuthenticateSessionReply (20)
+                    if (obj.amtaccumulator.length < 9) return;
+                    var authDataLen = obj.amtaccumulator.readInt32LE(5);
+                    if (obj.amtaccumulator.length < 9 + authDataLen) return;
+                    var status = obj.amtaccumulator[1];
+                    var authType = obj.amtaccumulator[4];
+                    var authData = [];
+                    for (i = 0; i < authDataLen; i++) { authData.push(obj.amtaccumulator[9 + i]); }
+                    var authDataBuf = obj.amtaccumulator.slice(9, 9 + authDataLen);
+                    cmdsize = 9 + authDataLen;
+                    if (authType == 0) {
+                        // Query
+                        if (authData.indexOf(4) >= 0) {
+                            // Good Digest Auth (With cnonce and all)
+                            obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x04) + IntToStrX(obj.user.length + obj.authuri.length + 8) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(0x00, 0x00) + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(0x00, 0x00, 0x00, 0x00));
+                        }
+                        /*
+                        else if (authData.indexOf(3) >= 0) {
+                            // Bad Digest Auth (Not sure why this is supported, cnonce is not used!)
+                            obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x03) + IntToStrX(obj.user.length + obj.authuri.length + 7) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(0x00, 0x00) + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(0x00, 0x00, 0x00));
+                        }
+                        else if (authData.indexOf(1) >= 0) {
+                            // Basic Auth (Probably a good idea to not support this unless this is an old version of Intel AMT)
+                            obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x01) + IntToStrX(obj.user.length + obj.pass.length + 2) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(obj.pass.length) + obj.pass);
+                        }
+                        */
+                        else obj.Stop();
+                    }
+                    else if ((authType == 3 || authType == 4) && status == 1) {
+                        var curptr = 0;
+
+                        // Realm
+                        var realmlen = authDataBuf[curptr];
+                        var realm = authDataBuf.slice(curptr + 1, curptr + 1 + realmlen).toString();
+                        curptr += (realmlen + 1);
+
+                        // Check the digest realm. If it does not match, close the connection.
+                        if (obj.digestRealmMatch && (obj.digestRealmMatch != realm)) { obj.Stop(); return; }
+
+                        // Nonce
+                        var noncelen = authDataBuf[curptr];
+                        var nonce = authDataBuf.slice(curptr + 1, curptr + 1 + noncelen).toString();
+                        curptr += (noncelen + 1);
+
+                        // QOP
+                        var qoplen = 0;
+                        var qop = null;
+                        var cnonce = obj.xxRandomValueHex(32);
+                        var snc = '00000002';
+                        var extra = '';
+                        if (authType == 4) {
+                            qoplen = authDataBuf[curptr];
+                            qop = authDataBuf.slice(curptr + 1, curptr + 1 + qoplen).toString();
+                            curptr += (qoplen + 1);
+                            extra = snc + ':' + cnonce + ':' + qop + ':';
+                        }
+                        var digest = hex_md5(hex_md5(obj.user + ':' + realm + ':' + obj.pass) + ':' + nonce + ':' + extra + hex_md5('POST:' + obj.authuri));
+                        var totallen = obj.user.length + realm.length + nonce.length + obj.authuri.length + cnonce.length + snc.length + digest.length + 7;
+                        if (authType == 4) totallen += (qop.length + 1);
+                        var buf = Buffer.concat([new Buffer([0x13, 0x00, 0x00, 0x00, authType]), new Buffer([totallen & 0xFF, (totallen >> 8) & 0xFF, 0x00, 0x00]), new Buffer([obj.user.length]), new Buffer(obj.user), new Buffer([realm.length]), new Buffer(realm), new Buffer([nonce.length]), new Buffer(nonce), new Buffer([obj.authuri.length]), new Buffer(obj.authuri), new Buffer([cnonce.length]), new Buffer(cnonce), new Buffer([snc.length]), new Buffer(snc), new Buffer([digest.length]), new Buffer(digest)]);
+                        if (authType == 4) buf = Buffer.concat([buf, new Buffer([qop.length]), new Buffer(qop) ]);
+                        obj.xxSend(buf);
+                    }
+                    else if (status == 0) { // Success
+                        if (obj.protocol == 1) {
+                            // Serial-over-LAN: Send Intel AMT serial settings...
+                            var MaxTxBuffer = 10000;
+                            var TxTimeout = 100;
+                            var TxOverflowTimeout = 0;
+                            var RxTimeout = 10000;
+                            var RxFlushTimeout = 100;
+                            var Heartbeat = 0;//5000;
+                            obj.xxSend(String.fromCharCode(0x20, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++) + ToShortStr(MaxTxBuffer) + ToShortStr(TxTimeout) + ToShortStr(TxOverflowTimeout) + ToShortStr(RxTimeout) + ToShortStr(RxFlushTimeout) + ToShortStr(Heartbeat) + ToIntStr(0));
+                        }
+                        if (obj.protocol == 2) {
+                            // Remote Desktop: Send traffic directly...
+                            obj.xxSend(new Buffer([0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
+                        }
+                        if (obj.protocol == 3) {
+                            // Remote IDER: Send traffic directly...
+                            obj.connectstate = 1;
+                            obj.xxStateChange(3);
+                        }
+                    } else obj.Stop();
+                    break;
+                case 0x21: // Response to settings (33)
+                    if (obj.amtaccumulator.length < 23) break;
+                    cmdsize = 23;
+                    obj.xxSend(String.fromCharCode(0x27, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++) + String.fromCharCode(0x00, 0x00, 0x1B, 0x00, 0x00, 0x00));
+                    if (obj.protocol == 1) { obj.amtkeepalivetimer = setInterval(obj.xxSendAmtKeepAlive, 2000); }
+                    obj.connectstate = 1;
+                    obj.xxStateChange(3);
+                    break;
+                case 0x29: // Serial Settings (41)
+                    if (obj.amtaccumulator.length < 10) break;
+                    cmdsize = 10;
+                    break;
+                case 0x2A: // Incoming display data (42)
+                    if (obj.amtaccumulator.length < 10) break;
+                    var cs = (10 + ((obj.amtaccumulator[9] & 0xFF) << 8) + (obj.amtaccumulator[8] & 0xFF));
+                    if (obj.amtaccumulator.length < cs) break;
+                    obj.m.ProcessData(obj.amtaccumulator.slice(10, cs));
+                    cmdsize = cs;
+                    break;
+                case 0x2B: // Keep alive message (43)
+                    if (obj.amtaccumulator.length < 8) break;
+                    cmdsize = 8;
+                    break;
+                case 0x41:
+                    if (obj.amtaccumulator.length < 8) break;
+                    obj.connectstate = 1;
+                    obj.m.Start();
+                    // KVM traffic, forward rest of accumulator directly.
+                    if (obj.amtaccumulator.length > 8) { obj.m.ProcessData(obj.amtaccumulator.substring(8)); }
+                    cmdsize = obj.amtaccumulator.length;
+                    break;
+                default:
+                    console.log('Unknown Intel AMT command: ' + obj.amtaccumulator[0] + ' acclen=' + obj.amtaccumulator.length);
+                    obj.Stop();
+                    return;
+            }
+            if (cmdsize == 0) return;
+            obj.amtaccumulator = obj.amtaccumulator.slice(cmdsize);
+        }
+    }
+    
+    obj.xxSend = function (x) {
+        if (urlvars && urlvars['redirtrace']) { console.log('REDIR-SEND(' + x.length + '): ' + rstr2hex(x)); }
+        //obj.Debug('Send(' + x.length + '): ' + Buffer.from(x, 'binary').toString('hex'));
+        if (typeof x == 'string') { obj.socket.write(Buffer.from(x, 'binary')); } else { obj.socket.write(x); }
+    }
+
+    // Send Serial-over-LAN ASCII characters
+    obj.Send = function (x) {
+        if (obj.socket == null || obj.connectstate != 1) return;
+        if (obj.protocol == 1) { obj.xxSend(String.fromCharCode(0x28, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++) + ToShortStr(x.length) + x); } else { obj.xxSend(x); }
+    }
+
+    obj.xxSendAmtKeepAlive = function () {
+        if (obj.socket == null) return;
+        obj.xxSend(String.fromCharCode(0x2B, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++));
+    }
+
+    // Uses OpenSSL random to generate a hex string
+    obj.xxRandomValueHex = function (len) {
+        var t = [], l = Math.floor(len / 2);
+        for (var i = 0; i < l; i++) { t.push(obj.tls.generateRandomInteger('0', '255')); }
+        return new Buffer(t).toString('hex');
+    }
+
+    obj.xxOnSocketClosed = function () {
+        obj.socket = null;
+        if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CLOSED'); }
+        //obj.Debug('Socket Closed');
+        obj.Stop();
+    }
+
+    obj.xxStateChange = function(newstate) {
+        if (obj.State == newstate) return;
+        obj.State = newstate;
+        obj.m.xxStateChange(obj.State);
+        if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
+    }
+
+    obj.Stop = function () {
+        if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CLOSED'); }
+        //obj.Debug('Socket Stopped');
+        obj.xxStateChange(0);
+        obj.connectstate = -1;
+        obj.amtaccumulator = Buffer.alloc(0);
+        if (obj.socket != null) { obj.socket.destroy(); obj.socket = null; }
+        if (obj.amtkeepalivetimer != null) { clearInterval(obj.amtkeepalivetimer); obj.amtkeepalivetimer = null; }
+    }
+
+    obj.RedirectStartSol = new Buffer([0x10, 0x00, 0x00, 0x00, 0x53, 0x4F, 0x4C, 0x20]);
+    obj.RedirectStartKvm = new Buffer([0x10, 0x01, 0x00, 0x00, 0x4b, 0x56, 0x4d, 0x52]);
+    obj.RedirectStartIder = new Buffer([0x10, 0x00, 0x00, 0x00, 0x49, 0x44, 0x45, 0x52]);
+
+    return obj;
+}
+
+function ToIntStr(v) { return String.fromCharCode((v & 0xFF), ((v >> 8) & 0xFF), ((v >> 16) & 0xFF), ((v >> 24) & 0xFF)); }
+function ToShortStr(v) { return String.fromCharCode((v & 0xFF), ((v >> 8) & 0xFF)); }
+
+function ShortToStr(v) { return String.fromCharCode((v >> 8) & 0xFF, v & 0xFF); }
+function ShortToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF); }
+function IntToStr(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); }
+function IntToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); }
+
+var md5hasher = require('MD5Stream').create();
+function hex_md5(a) { return md5hasher.syncHash(a).toString('hex').toLowerCase(); }

+ 109 - 0
agents/modules_meshcmd/amt-scanner.js

@@ -0,0 +1,109 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/**
+* @description Meshcentral Intel AMT Local Scanner
+* @author Ylian Saint-Hilaire & Joko Sastriawan
+* @version v0.0.1
+*/
+
+// Construct a Intel AMT Scanner object
+
+function AMTScanner() {
+    var emitterUtils = require('events').inherits(this);
+    emitterUtils.createEvent('found');
+
+    this.dgram = require('dgram');
+
+    this.buildRmcpPing = function (tag) {
+        var packet = Buffer.from('06000006000011BE80000000', 'hex');
+        packet[9] = tag;
+        return packet;
+    };
+
+    this.parseRmcpPacket = function (server, data, rinfo, func) {
+        if (data == null || data.length < 20) return;
+        var res = {};
+        if (((data[12] == 0) || (data[13] != 0) || (data[14] != 1) || (data[15] != 0x57)) && (data[21] & 32)) {
+            res.servertag = data[9];
+            res.minorVersion = data[18] & 0x0F;
+            res.majorVersion = (data[18] >> 4) & 0x0F;
+            res.provisioningState = data[19] & 0x03; // Pre = 0, In = 1, Post = 2
+
+            var openPort = (data[16] * 256) + data[17];
+            var dualPorts = ((data[19] & 0x04) != 0) ? true : false;
+            res.openPorts = [openPort];
+            res.address = rinfo.address;
+            if (dualPorts == true) { res.openPorts = [16992, 16993]; }
+            if (func !== undefined) {
+                func(server, res);
+            }
+        }
+    }
+
+    this.parseIPv4Range = function (range) {
+        if (range == undefined || range == null) return null;
+        var x = range.split('-');
+        if (x.length == 2) { return { min: this.parseIpv4Addr(x[0]), max: this.parseIpv4Addr(x[1]) }; }
+        x = range.split('/');
+        if (x.length == 2) {
+            var ip = this.parseIpv4Addr(x[0]), masknum = parseInt(x[1]), mask = 0;
+            if (masknum <= 16 || masknum > 32) return null;
+            masknum = 32 - masknum;
+            for (var i = 0; i < masknum; i++) { mask = (mask << 1); mask++; }
+            return { min: (ip & (0xFFFFFFFF - mask))+1, max: (ip & (0xFFFFFFFF - mask)) + mask -1 };//remove network and broadcast address to avoid irrecoverable socket error
+        }
+        x = this.parseIpv4Addr(range);
+        if (x == null) return null;
+        return { min: x, max: x };
+    };
+
+    // Parse IP address. Takes a 
+    this.parseIpv4Addr = function (addr) {
+        var x = addr.split('.');
+        if (x.length == 4) { return (parseInt(x[0]) << 24) + (parseInt(x[1]) << 16) + (parseInt(x[2]) << 8) + (parseInt(x[3]) << 0); }
+        return null;
+    }
+
+    // IP address number to string
+    this.IPv4NumToStr = function (num) {
+        return ((num >> 24) & 0xFF) + '.' + ((num >> 16) & 0xFF) + '.' + ((num >> 8) & 0xFF) + '.' + (num & 0xFF);
+    }
+
+    this.scan = function (rangestr, timeout, callback) {
+        var iprange = this.parseIPv4Range(rangestr);
+        var rmcp = this.buildRmcpPing(0);
+        var server = this.dgram.createSocket({ type: 'udp4' });
+        server.parent = this;
+        server.scanResults = [];
+        server.on('error', function (err) { console.log('Error:' + err); });
+        server.on('message', function (msg, rinfo) { if (rinfo.size > 4) { this.parent.parseRmcpPacket(this, msg, rinfo, function (s, res) { s.scanResults.push(res); }) }; });
+        server.on('listening', function () { for (var i = iprange.min; i <= iprange.max; i++) {             
+            server.send(rmcp, 623, server.parent.IPv4NumToStr(i)); } });
+        server.bind({ address: '0.0.0.0', port: 0, exclusive: true });
+        var tmout = setTimeout(function cb() {
+            //console.log("Server closed");
+            server.close();
+            if (callback) {
+                callback(server.scanResults);
+            }
+            server.parent.emit('found', server.scanResults);
+            delete server;
+        }, timeout);
+    };
+}
+
+module.exports = AMTScanner;

+ 20 - 0
agents/modules_meshcmd/amt-sol.js

@@ -0,0 +1,20 @@
+/** 
+* @description Serial-over-LAN Handling Module
+* @author Ylian Saint-Hilaire
+*/
+
+// meshservice meshcmd.js amtterm --host 192.168.2.186 --pass P@ssw0rd
+
+// Construct a Intel AMT Serial-over-LAN object
+module.exports = function CreateAmtRemoteSol() {
+    var obj = {};
+    obj.protocol = 1; // Serial-over-LAN
+    obj.debug = false;
+    obj.onData = null;
+    obj.xxStateChange = function (newstate) { if (obj.debug) console.log('SOL-StateChange', newstate); if (newstate == 0) { obj.Stop(); } if (newstate == 3) { obj.Start(); } }
+    obj.Start = function () { if (obj.debug) { console.log('SOL-Start'); } }
+    obj.Stop = function () { if (obj.debug) { console.log('SOL-Stop'); } }
+    obj.ProcessData = function (data) { if (obj.debug) { console.log('SOL-ProcessData', data); } if (obj.onData) { obj.onData(obj, data); } }
+    obj.Send = function(text) { if (obj.debug) { console.log('SOL-Send', text); } obj.parent.Send(text); }
+    return obj;
+}

+ 141 - 0
agents/modules_meshcmd/amt-wsman-duk.js

@@ -0,0 +1,141 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/** 
+* @description WSMAN communication using duktape http
+* @author Ylian Saint-Hilaire
+* @version v0.2.0c
+*/
+
+// Construct a WSMAN communication object
+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
+    obj.MaxActiveAjaxCount = 1;         // Maximum number of activate AJAX calls at the same time.
+    obj.FailAllError = 0;               // Set this to non-zero to fail all AJAX calls with that error status, 999 causes responses to be silent.
+    obj.digest = null;
+    obj.RequestCount = 0;
+
+    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 {
+        obj.host = arguments[0];
+        obj.port = arguments[1];
+        obj.user = arguments[2];
+        obj.pass = arguments[3];
+        obj.tls = arguments[4];
+    }
+
+
+    // Private method
+    //   pri = priority, if set to 1, the call is high priority and put on top of the stack.
+    obj.PerformAjax = function (postdata, callback, tag, pri, url, action) {
+        if ((obj.ActiveAjaxCount == 0 || ((obj.ActiveAjaxCount < obj.MaxActiveAjaxCount) && (obj.challengeParams != null))) && obj.PendingAjax.length == 0) {
+            // There are no pending AJAX calls, perform the call now.
+            obj.PerformAjaxEx(postdata, callback, tag, url, action);
+        } else {
+            // If this is a high priority call, put this call in front of the array, otherwise put it in the back.
+            if (pri == 1) { obj.PendingAjax.unshift([postdata, callback, tag, url, action]); } else { obj.PendingAjax.push([postdata, callback, tag, url, action]); }
+        }
+    }
+
+    // Private method
+    obj.PerformNextAjax = function () {
+        if (obj.ActiveAjaxCount >= obj.MaxActiveAjaxCount || obj.PendingAjax.length == 0) return;
+        var x = obj.PendingAjax.shift();
+        obj.PerformAjaxEx(x[0], x[1], x[2], x[3], x[4]);
+        obj.PerformNextAjax();
+    }
+
+    // Private method
+    obj.PerformAjaxEx = function (postdata, callback, tag, url, action)
+    {
+        if (obj.FailAllError != 0) { if (obj.FailAllError != 999) { obj.gotNextMessagesError({ status: obj.FailAllError }, 'error', null, [postdata, callback, tag]); } return; }
+        if (!postdata) postdata = "";
+        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) {
+                obj.digest = require('http-digest').create({ authToken: obj.authToken });
+            } else {
+                obj.digest = require('http-digest').create(obj.user, obj.pass);
+            }
+            obj.digest.http = require('http');
+        }
+        var request = { delayWrite: true, protocol: (obj.tls == 1 ? 'https:' : 'http:'), method: 'POST', host: obj.host, path: '/wsman', port: obj.port, rejectUnauthorized: false, checkServerIdentity: function (cert) { /*console.log('checkServerIdentity', JSON.stringify(cert));*/ } };
+        var req = obj.digest.request(request);
+        //console.log('Request ' + (obj.RequestCount++));
+        if (globalDebugFlags & 1) { console.log('Request ' + (obj.RequestCount++)); } // DEBUG
+
+        req.on('error', function (err) { obj.gotNextMessagesError({ status: 600, error: '' + err }, 'error', null, [postdata, callback, tag]); });
+        req.on('response', function (response) {
+            //console.log(JSON.stringify(response, null, 2));
+            if (globalDebugFlags & 1) { console.log('Response: ' + response.statusCode); }
+            if (response.statusCode != 200) {
+                if (globalDebugFlags & 1) { console.log('ERR:' + JSON.stringify(response)); }
+                obj.gotNextMessagesError({ status: response.statusCode }, 'error', null, [postdata, callback, tag]);
+            } else {
+                response.acc = '';
+                response.on('data', function (data2) { this.acc += data2; });
+                response.on('end', function () { obj.gotNextMessages(response.acc, 'success', { status: response.statusCode }, [postdata, callback, tag]); });
+            }
+        });
+
+        // Send POST body, this work with binary.
+        req.end(postdata);
+        obj.ActiveAjaxCount++;
+        return req;
+    }
+
+    // AJAX specific private method
+    obj.pendingAjaxCall = [];
+
+    // Private method
+    obj.gotNextMessages = function (data, status, request, callArgs) {
+        obj.ActiveAjaxCount--;
+        if (obj.FailAllError == 999) return;
+        if (globalDebugFlags & 1) { console.log("RECV: " + data + "\r\n\r\n"); } // DEBUG
+        if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; }
+        if (request.status != 200) { callArgs[1](null, request.status, callArgs[2]); return; }
+        callArgs[1](data, 200, callArgs[2]);
+        obj.PerformNextAjax();
+    }
+
+    // Private method
+    obj.gotNextMessagesError = function (request, status, errorThrown, callArgs) {
+        obj.ActiveAjaxCount--;
+        if (obj.FailAllError == 999) return;
+        if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; }
+        //if (status != 200) { console.log("ERROR, status=" + status + "\r\n\r\nreq=" + callArgs[0]); } // Debug: Display the request & response if something did not work.
+        if (obj.FailAllError != 999) { callArgs[1]({ Header: { HttpError: request.status, error: request.error } }, request.status, callArgs[2]); }
+        obj.PerformNextAjax();
+    }
+
+    // Cancel all pending queries with given status
+    obj.CancelAllQueries = function (s) {
+        while (obj.PendingAjax.length > 0) { var x = obj.PendingAjax.shift(); x[1](null, s, x[2]); }
+    }
+
+    return obj;
+}
+
+module.exports = CreateWsmanComm;

+ 211 - 0
agents/modules_meshcmd/amt-wsman.js

@@ -0,0 +1,211 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/** 
+* @description Intel(r) AMT WSMAN Stack
+* @author Ylian Saint-Hilaire
+* @version v0.2.0
+*/
+
+// Construct a MeshServer object
+function WsmanStackCreateService(/*CreateWsmanComm, host, port, user, pass, tls, extra*/)
+{
+    var obj = {_ObjectID: 'WSMAN'};
+    //obj.onDebugMessage = null;          // Set to a function if you want to get debug messages.
+    obj.NextMessageId = 1;              // Next message number, used to label WSMAN calls.
+    obj.Address = '/wsman';
+    obj.xmlParser = require('amt-xml');
+
+    if (arguments.length == 1 && typeof (arguments[0] == 'object'))
+    {
+        var CreateWsmanComm = arguments[0].transport;
+        if (CreateWsmanComm) { obj.comm = new CreateWsmanComm(arguments[0]); }
+    }
+    else
+    {
+        var CreateWsmanComm = arguments[0];
+        if (CreateWsmanComm) { obj.comm = new CreateWsmanComm(arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]); }
+    }
+
+    obj.PerformAjax = function PerformAjax(postdata, callback, tag, pri, namespaces) {
+        if (namespaces == null) namespaces = '';
+        obj.comm.PerformAjax('<?xml version=\"1.0\" encoding=\"utf-8\"?><Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns=\"http://www.w3.org/2003/05/soap-envelope\" ' + namespaces + '><Header><a:Action>' + postdata, function (data, status, tag) {
+            if (status != 200) { callback(obj, null, (data != null) ? data : { Header: { HttpError: status } }, status, tag); return; }
+            var wsresponse = obj.xmlParser.ParseWsman(data);
+            if (!wsresponse || wsresponse == null) { callback(obj, null, { Header: { HttpError: status } }, 601, tag); } else { callback(obj, wsresponse.Header["ResourceURI"], wsresponse, 200, tag); }
+        }, tag, pri);
+    }
+
+    // Private method
+    //obj.Debug = function (msg) { /*console.log(msg);*/ }
+
+    // Cancel all pending queries with given status
+    obj.CancelAllQueries = function CancelAllQueries(s) { obj.comm.CancelAllQueries(s); }
+
+    // Get the last element of a URI string
+    obj.GetNameFromUrl = function (resuri) {
+        var x = resuri.lastIndexOf("/");
+        return (x == -1)?resuri:resuri.substring(x + 1);
+    }
+
+    // Perform a WSMAN Subscribe operation
+    obj.ExecSubscribe = function ExecSubscribe(resuri, delivery, url, callback, tag, pri, selectors, opaque, user, pass) {
+        var digest = "", digest2 = "", opaque = "";
+        if (user != null && pass != null) { digest = '<t:IssuedTokens xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:se="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><t:RequestSecurityTokenResponse><t:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken</t:TokenType><t:RequestedSecurityToken><se:UsernameToken><se:Username>' + user + '</se:Username><se:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#PasswordText">' + pass + '</se:Password></se:UsernameToken></t:RequestedSecurityToken></t:RequestSecurityTokenResponse></t:IssuedTokens>'; digest2 = '<w:Auth Profile="http://schemas.dmtf.org/wbem/wsman/1/wsman/secprofile/http/digest"/>'; }
+        if (opaque != null) { opaque = '<a:ReferenceParameters><m:arg>' + opaque + '</m:arg></a:ReferenceParameters>'; }
+        if (delivery == 'PushWithAck') { delivery = 'dmtf.org/wbem/wsman/1/wsman/PushWithAck'; } else if (delivery == 'Push') { delivery = 'xmlsoap.org/ws/2004/08/eventing/DeliveryModes/Push'; }
+        var data = "http://schemas.xmlsoap.org/ws/2004/08/eventing/Subscribe</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" + _PutObjToSelectorsXml(selectors) + digest + '</Header><Body><e:Subscribe><e:Delivery Mode="http://schemas.' + delivery + '"><e:NotifyTo><a:Address>' + url + '</a:Address>' + opaque + '</e:NotifyTo>' + digest2 + '</e:Delivery></e:Subscribe>';
+        obj.PerformAjax(data + "</Body></Envelope>", callback, tag, pri, 'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing" xmlns:m="http://x.com"');
+    }
+
+    // Perform a WSMAN UnSubscribe operation
+    obj.ExecUnSubscribe = function ExecUnSubscribe(resuri, callback, tag, pri, selectors) {
+        var data = "http://schemas.xmlsoap.org/ws/2004/08/eventing/Unsubscribe</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" + _PutObjToSelectorsXml(selectors) + '</Header><Body><e:Unsubscribe/>';
+        obj.PerformAjax(data + "</Body></Envelope>", callback, tag, pri, 'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing"');
+    }
+
+    // Perform a WSMAN PUT operation
+    obj.ExecPut = function ExecPut(resuri, putobj, callback, tag, pri, selectors) {
+        var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Put</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60.000S</w:OperationTimeout>" + _PutObjToSelectorsXml(selectors) + '</Header><Body>' + _PutObjToBodyXml(resuri, putobj);
+        obj.PerformAjax(data + "</Body></Envelope>", callback, tag, pri);
+    }
+		
+    // Perform a WSMAN CREATE operation
+    obj.ExecCreate = function ExecCreate(resuri, putobj, callback, tag, pri, selectors) {
+        var objname = obj.GetNameFromUrl(resuri);
+        var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Create</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout>" + _PutObjToSelectorsXml(selectors) + "</Header><Body><g:" + objname + " xmlns:g=\"" + resuri + "\">";
+        for (var n in putobj) { data += "<g:" + n + ">" + putobj[n] + "</g:" + n + ">" } 
+        obj.PerformAjax(data + "</g:" + objname + "></Body></Envelope>", callback, tag, pri);
+    }
+
+    // Perform a WSMAN DELETE operation
+    obj.ExecDelete = function ExecDelete(resuri, putobj, callback, tag, pri) {
+        var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout>" + _PutObjToSelectorsXml(putobj) + "</Header><Body /></Envelope>";
+        obj.PerformAjax(data, callback, tag, pri);
+    }
+
+    // Perform a WSMAN GET operation
+    obj.ExecGet = function ExecGet(resuri, callback, tag, pri) {
+        obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Get</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout></Header><Body /></Envelope>", callback, tag, pri);
+    }
+
+	// Perform a WSMAN method call operation
+    obj.ExecMethod = function ExecMethod(resuri, method, args, callback, tag, pri, selectors) {
+        var argsxml = "";
+        for (var i in args) { if (args[i] != null) { if (Array.isArray(args[i])) { for (var x in args[i]) { argsxml += "<r:" + i + ">" + args[i][x] + "</r:" + i + ">"; } } else { argsxml += "<r:" + i + ">" + args[i] + "</r:" + i + ">"; } } }
+        obj.ExecMethodXml(resuri, method, argsxml, callback, tag, pri, selectors);
+    }
+	
+    // Perform a WSMAN method call operation. The arguments are already formatted in XML.
+    obj.ExecMethodXml = function ExecMethodXml(resuri, method, argsxml, callback, tag, pri, selectors) {
+        obj.PerformAjax(resuri + "/" + method + "</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout>" + _PutObjToSelectorsXml(selectors) + "</Header><Body><r:" + method + '_INPUT' + " xmlns:r=\"" + resuri + "\">" + argsxml + "</r:" + method + "_INPUT></Body></Envelope>", callback, tag, pri);
+    }
+
+    // Perform a WSMAN ENUM operation
+    obj.ExecEnum = function ExecEnum(resuri, callback, tag, pri) {
+        obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout></Header><Body><Enumerate xmlns=\"http://schemas.xmlsoap.org/ws/2004/09/enumeration\" /></Body></Envelope>", callback, tag, pri);
+    }
+
+    // Perform a WSMAN PULL operation
+    obj.ExecPull = function ExecPull(resuri, enumctx, callback, tag, pri) {
+        obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout></Header><Body><Pull xmlns=\"http://schemas.xmlsoap.org/ws/2004/09/enumeration\"><EnumerationContext>" + enumctx + "</EnumerationContext><MaxElements>999</MaxElements><MaxCharacters>99999</MaxCharacters></Pull></Body></Envelope>", callback, tag, pri);
+    }
+
+    function _PutObjToBodyXml(resuri, putObj) {
+        if (!resuri || putObj == null) return '';
+		var objname = obj.GetNameFromUrl(resuri);
+		var result = '<r:' + objname + ' xmlns:r="' + resuri + '">';
+
+		for (var prop in putObj) {
+			if (!putObj.hasOwnProperty(prop) || prop.indexOf('__') === 0 || prop.indexOf('@') === 0) continue;
+			if (putObj[prop] == null || typeof putObj[prop] === 'function') continue;
+			if (typeof putObj[prop] === 'object' && putObj[prop]['ReferenceParameters']) {
+				result += '<r:' + prop + '><a:Address>' + putObj[prop].Address + '</a:Address><a:ReferenceParameters><w:ResourceURI>' + putObj[prop]['ReferenceParameters']["ResourceURI"] + '</w:ResourceURI><w:SelectorSet>';
+				var selectorArray = putObj[prop]['ReferenceParameters']['SelectorSet']['Selector'];
+				if (Array.isArray(selectorArray)) {
+					for (var i=0; i< selectorArray.length; i++) {
+						result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray[i]) + '>' + selectorArray[i]['Value'] + '</w:Selector>';
+					}
+				}
+				else {
+					result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray) + '>' + selectorArray['Value'] + '</w:Selector>';
+				}
+				result += '</w:SelectorSet></a:ReferenceParameters></r:' + prop + '>';
+			}
+			else {
+			    if (Array.isArray(putObj[prop])) {
+			        for (var i = 0; i < putObj[prop].length; i++) {
+			            result += '<r:' + prop + '>' + putObj[prop][i].toString() + '</r:' + prop + '>';
+			        }
+			    } else {
+			        result += '<r:' + prop + '>' + putObj[prop].toString() + '</r:' + prop + '>';
+			    }
+			}
+		}
+
+		result += '</r:' + objname + '>';
+		return result;
+	}
+
+	/* 
+	convert 
+		{ @Name: 'InstanceID', @AttrName: 'Attribute Value'}
+	into
+		' Name="InstanceID" AttrName="Attribute Value" '
+	*/
+    function _ObjectToXmlAttributes(objWithAttributes) {
+		if(!objWithAttributes) return '';
+		var result = ' ';
+		for (var propName in objWithAttributes) {
+			if (!objWithAttributes.hasOwnProperty(propName) || propName.indexOf('@') !== 0) continue;
+			result += propName.substring(1) + '="' + objWithAttributes[propName] + '" ';
+		}
+		return result;
+	}
+
+    function _PutObjToSelectorsXml(selectorSet) {
+        if ((selectorSet == null) || (selectorSet == 'null')) return '';
+        if (typeof selectorSet == 'string') return selectorSet;
+        if (selectorSet['InstanceID']) return "<w:SelectorSet><w:Selector Name=\"InstanceID\">" + selectorSet['InstanceID'] + "</w:Selector></w:SelectorSet>";
+		var result = '<w:SelectorSet>';
+		for(var propName in selectorSet) {
+		    if (!selectorSet.hasOwnProperty(propName)) continue;
+		    result += '<w:Selector Name="' + propName + '">';
+		    if (selectorSet[propName]['ReferenceParameters']) {
+		        result += '<a:EndpointReference>';
+		        result += '<a:Address>' + selectorSet[propName]['Address'] + '</a:Address><a:ReferenceParameters><w:ResourceURI>' + selectorSet[propName]['ReferenceParameters']['ResourceURI'] + '</w:ResourceURI><w:SelectorSet>';
+		        var selectorArray = selectorSet[propName]['ReferenceParameters']['SelectorSet']['Selector'];
+		        if (Array.isArray(selectorArray)) {
+		            for (var i = 0; i < selectorArray.length; i++) {
+		                result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray[i]) + '>' + selectorArray[i]['Value'] + '</w:Selector>';
+		            }
+		        } else {
+		            result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray) + '>' + selectorArray['Value'] + '</w:Selector>';
+		        }
+		        result += '</w:SelectorSet></a:ReferenceParameters></a:EndpointReference>';
+		    } else {
+		        result += selectorSet[propName];
+		    }
+			result += '</w:Selector>';
+		}
+		result += '</w:SelectorSet>';
+		return result;
+	}
+
+    return obj;
+}
+
+module.exports = WsmanStackCreateService;

+ 189 - 0
agents/modules_meshcmd/amt-xml.js

@@ -0,0 +1,189 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+try { Object.defineProperty(Array.prototype, "peek", { value: function () { return (this.length > 0 ? this[this.length - 1] : undefined); } }); } catch (e) { }
+
+
+// Parse XML and return JSON
+module.exports.ParseWsman = function (xml) {
+    try {
+        if (!xml.childNodes) xml = _turnToXml(xml);
+        var r = { Header: {} }, header = xml.getElementsByTagName("Header")[0], t;
+        if (!header) header = xml.getElementsByTagName("a:Header")[0];
+        if (!header) return null;
+        for (var i = 0; i < header.childNodes.length; i++) {
+            var child = header.childNodes[i];
+            r.Header[child.localName] = child.textContent;
+        }
+        var body = xml.getElementsByTagName("Body")[0];
+        if (!body) body = xml.getElementsByTagName("a:Body")[0];
+        if (!body) return null;
+        if (body.childNodes.length > 0) {
+            t = body.childNodes[0].localName;
+            var x = t.indexOf('_OUTPUT');
+            if ((x != -1) && (x == (t.length - 7))) { t = t.substring(0, t.length - 7); }
+            r.Header['Method'] = t;
+            r.Body = _ParseWsmanRec(body.childNodes[0]);
+        }
+        return r;
+    } catch (e) {
+        console.error("Unable to parse XML: " + xml, e);
+        return null;
+    }
+}
+
+// Private method
+function _ParseWsmanRec(node) {
+    var data, r = {};
+    for (var i = 0; i < node.childNodes.length; i++) {
+        var child = node.childNodes[i];
+        if ((child.childElementCount == null) || (child.childElementCount == 0)) { data = child.textContent; } else { data = _ParseWsmanRec(child); }
+        if (data == 'true') data = true; // Convert 'true' into true
+        if (data == 'false') data = false; // Convert 'false' into false
+        if ((parseInt(data) + '') === data) data = parseInt(data); // Convert integers
+
+        var childObj = data;
+        if ((child.attributes != null) && (child.attributes.length > 0)) {
+            childObj = { 'Value': data };
+            for (var j = 0; j < child.attributes.length; j++) {
+                childObj['@' + child.attributes[j].name] = child.attributes[j].value;
+            }
+        }
+
+        if (r[child.localName] instanceof Array) { r[child.localName].push(childObj); }
+        else if (r[child.localName] == null) { r[child.localName] = childObj; }
+        else { r[child.localName] = [r[child.localName], childObj]; }
+    }
+    return r;
+}
+
+function _PutObjToBodyXml(resuri, putObj) {
+    if (!resuri || putObj == null) return '';
+    var objname = obj.GetNameFromUrl(resuri);
+    var result = '<r:' + objname + ' xmlns:r="' + resuri + '">';
+
+    for (var prop in putObj) {
+        if (!putObj.hasOwnProperty(prop) || prop.indexOf('__') === 0 || prop.indexOf('@') === 0) continue;
+        if (putObj[prop] == null || typeof putObj[prop] === 'function') continue;
+        if (typeof putObj[prop] === 'object' && putObj[prop]['ReferenceParameters']) {
+            result += '<r:' + prop + '><a:Address>' + putObj[prop].Address + '</a:Address><a:ReferenceParameters><w:ResourceURI>' + putObj[prop]['ReferenceParameters']["ResourceURI"] + '</w:ResourceURI><w:SelectorSet>';
+            var selectorArray = putObj[prop]['ReferenceParameters']['SelectorSet']['Selector'];
+            if (Array.isArray(selectorArray)) {
+                for (var i = 0; i < selectorArray.length; i++) {
+                    result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray[i]) + '>' + selectorArray[i]['Value'] + '</w:Selector>';
+                }
+            }
+            else {
+                result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray) + '>' + selectorArray['Value'] + '</w:Selector>';
+            }
+            result += '</w:SelectorSet></a:ReferenceParameters></r:' + prop + '>';
+        }
+        else {
+            if (Array.isArray(putObj[prop])) {
+                for (var i = 0; i < putObj[prop].length; i++) {
+                    result += '<r:' + prop + '>' + putObj[prop][i].toString() + '</r:' + prop + '>';
+                }
+            } else {
+                result += '<r:' + prop + '>' + putObj[prop].toString() + '</r:' + prop + '>';
+            }
+        }
+    }
+
+    result += '</r:' + objname + '>';
+    return result;
+}
+
+// This is a drop-in replacement to _turnToXml() that works without xml parser dependency.
+function _treeBuilder() {
+    this.tree = [];
+    this.push = function (element) { this.tree.push(element); };
+    this.pop = function () { var element = this.tree.pop(); if (this.tree.length > 0) { var x = this.tree.peek(); x.childNodes.push(element); x.childElementCount = x.childNodes.length; } return (element); };
+    this.peek = function () { return (this.tree.peek()); }
+    this.addNamespace = function (prefix, namespace) { this.tree.peek().nsTable[prefix] = namespace; if (this.tree.peek().attributes.length > 0) { for (var i = 0; i < this.tree.peek().attributes; ++i) { var a = this.tree.peek().attributes[i]; if (prefix == '*' && a.name == a.localName) { a.namespace = namespace; } else if (prefix != '*' && a.name != a.localName) { var pfx = a.name.split(':')[0]; if (pfx == prefix) { a.namespace = namespace; } } } } }
+    this.getNamespace = function (prefix) { for (var i = this.tree.length - 1; i >= 0; --i) { if (this.tree[i].nsTable[prefix] != null) { return (this.tree[i].nsTable[prefix]); } } return null; }
+}
+function _turnToXml(text) { if (text == null) return null; return ({ childNodes: [_turnToXmlRec(text)], getElementsByTagName: _getElementsByTagName, getChildElementsByTagName: _getChildElementsByTagName, getElementsByTagNameNS: _getElementsByTagNameNS }); }
+function _getElementsByTagNameNS(ns, name) { var ret = []; _xmlTraverseAllRec(this.childNodes, function (node) { if (node.localName == name && (node.namespace == ns || ns == '*')) { ret.push(node); } }); return ret; }
+function _getElementsByTagName(name) { var ret = []; _xmlTraverseAllRec(this.childNodes, function (node) { if (node.localName == name) { ret.push(node); } }); return ret; }
+function _getChildElementsByTagName(name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name) { ret.push(this.childNodes[node]); } } } return (ret); }
+function _getChildElementsByTagNameNS(ns, name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name && (ns == '*' || this.childNodes[node].namespace == ns)) { ret.push(this.childNodes[node]); } } } return (ret); }
+function _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } }
+function _turnToXmlRec(text) {
+    try {
+        if (text == null) return null;
+        var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
+        for (var i in x1) {
+            var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0];
+            if ((elementName.length > 0) && (elementName[0] != '?')) {
+                if (elementName[0] != '/') {
+                    var attributes = [], localName, localname2 = elementName.split(' ')[0].split(':'), localName = (localname2.length > 1) ? localname2[1] : localname2[0];
+                    Object.defineProperty(attributes, "get",
+                        {
+                            value: function () {
+                                if (arguments.length == 1) {
+                                    for (var a in this) { if (this[a].name == arguments[0]) { return (this[a]); } }
+                                }
+                                else if (arguments.length == 2) {
+                                    for (var a in this) { if (this[a].name == arguments[1] && (arguments[0] == '*' || this[a].namespace == arguments[0])) { return (this[a]); } }
+                                }
+                                else {
+                                    throw ('attributes.get(): Invalid number of parameters');
+                                }
+                            }
+                        });
+                    elementStack.push({ name: elementName, localName: localName, getChildElementsByTagName: _getChildElementsByTagName, getElementsByTagNameNS: _getElementsByTagNameNS, getChildElementsByTagNameNS: _getChildElementsByTagNameNS, attributes: attributes, childNodes: [], nsTable: {} });
+                    // Parse Attributes
+                    if (x3.length > 0) {
+                        var skip = false;
+                        for (var j in x3) {
+                            if (x3[j] == '/') {
+                                // This is an empty Element
+                                elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
+                                elementStack.peek().textContent = '';
+                                lastElement = elementStack.pop();
+                                skip = true;
+                                break;
+                            }
+                            var k = x3[j].indexOf('=');
+                            if (k > 0) {
+                                var attrName = x3[j].substring(0, k);
+                                var attrValue = x3[j].substring(k + 2, x3[j].length - 1);
+                                var attrNS = elementStack.getNamespace('*');
+
+                                if (attrName == 'xmlns') {
+                                    elementStack.addNamespace('*', attrValue);
+                                    attrNS = attrValue;
+                                } else if (attrName.startsWith('xmlns:')) {
+                                    elementStack.addNamespace(attrName.substring(6), attrValue);
+                                } else {
+                                    var ax = attrName.split(':');
+                                    if (ax.length == 2) { attrName = ax[1]; attrNS = elementStack.getNamespace(ax[0]); }
+                                }
+                                var x = { name: attrName, value: attrValue }
+                                if (attrNS != null) x.namespace = attrNS;
+                                elementStack.peek().attributes.push(x);
+                            }
+                        }
+                        if (skip) { continue; }
+                    }
+                    elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
+                    if (x2[1]) { elementStack.peek().textContent = x2[1]; }
+                } else { lastElement = elementStack.pop(); }
+            }
+        }
+    } catch (ex) { return null; }
+    return lastElement;
+}

+ 1057 - 0
agents/modules_meshcmd/amt.js

@@ -0,0 +1,1057 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/**
+* @fileoverview Intel(r) AMT Communication StackXX
+* @author Ylian Saint-Hilaire
+* @version v0.2.0b
+*/
+
+/**
+ * Construct a AmtStackCreateService object, this ia the main Intel AMT communication stack.
+ * @constructor
+ */
+function AmtStackCreateService(wsmanStack) {
+    var obj = new Object();
+    obj._ObjectID = 'AMT'
+    obj.wsman = wsmanStack;
+    obj.pfx = ["http://intel.com/wbem/wscim/1/amt-schema/1/", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/", "http://intel.com/wbem/wscim/1/ips-schema/1/"];
+    obj.PendingEnums = [];
+    obj.PendingBatchOperations = 0;
+    obj.ActiveEnumsCount = 0;
+    obj.MaxActiveEnumsCount = 1; // Maximum number of enumerations that can be done at the same time.
+    obj.onProcessChanged = null;
+    var _MaxProcess = 0;
+    var _LastProcess = 0;
+
+    // Return the number of pending actions
+    obj.GetPendingActions = function () { return (obj.PendingEnums.length * 2) + (obj.ActiveEnumsCount) + obj.wsman.comm.PendingAjax.length + obj.wsman.comm.ActiveAjaxCount + obj.PendingBatchOperations; }
+
+    // Private Method, Update the current processing status, this gives the application an idea of what progress is being done by the WSMAN stack
+    function _up() {
+        var x = obj.GetPendingActions();
+        if (_MaxProcess < x) _MaxProcess = x;
+        if (obj.onProcessChanged != null && _LastProcess != x) {
+            //console.log("Process Old=" + _LastProcess + ", New=" + x + ", PEnums=" + obj.PendingEnums.length + ", AEnums=" + obj.ActiveEnumsCount + ", PAjax=" + obj.wsman.comm.PendingAjax.length + ", AAjax=" + obj.wsman.comm.ActiveAjaxCount + ", PBatch=" + obj.PendingBatchOperations);
+            _LastProcess = x;
+            obj.onProcessChanged(x, _MaxProcess);
+        }
+        if (x == 0) _MaxProcess = 0;
+    }
+
+    // Perform a WSMAN "SUBSCRIBE" operation.
+    obj.Subscribe = function Subscribe(name, delivery, url, callback, tag, pri, selectors, opaque, user, pass) { obj.wsman.ExecSubscribe(obj.CompleteName(name), delivery, url, function (ws, resuri, response, xstatus) { _up(); callback.call(obj, obj, name, response, xstatus, tag); }, 0, pri, selectors, opaque, user, pass); _up(); }
+
+    // Perform a WSMAN "UNSUBSCRIBE" operation.
+    obj.UnSubscribe = function UnSubscribe(name, callback, tag, pri, selectors) { obj.wsman.ExecUnSubscribe(obj.CompleteName(name), function (ws, resuri, response, xstatus) { _up(); callback.call(obj, obj, name, response, xstatus, tag); }, 0, pri, selectors); _up(); }
+
+    // Perform a WSMAN "GET" operation.
+    obj.Get = function Get(name, callback, tag, pri) { obj.wsman.ExecGet(obj.CompleteName(name), function (ws, resuri, response, xstatus) { _up(); callback.call(obj, obj, name, response, xstatus, tag); }, 0, pri); _up(); }
+
+    // Perform a WSMAN "PUT" operation.
+    obj.Put = function Put(name, putobj, callback, tag, pri, selectors) { obj.wsman.ExecPut(obj.CompleteName(name), putobj, function (ws, resuri, response, xstatus) { _up(); callback.call(obj, obj, name, response, xstatus, tag); }, 0, pri, selectors); _up(); }
+	
+    // Perform a WSMAN "CREATE" operation.
+    obj.Create = function Create(name, putobj, callback, tag, pri) { obj.wsman.ExecCreate(obj.CompleteName(name), putobj, function (ws, resuri, response, xstatus) { _up(); callback.call(obj, obj, name, response, xstatus, tag); }, 0, pri); _up(); }
+
+    // Perform a WSMAN "DELETE" operation.
+    obj.Delete = function Delete(name, putobj, callback, tag, pri) { obj.wsman.ExecDelete(obj.CompleteName(name), putobj, function (ws, resuri, response, xstatus) { _up(); callback.call(obj, obj, name, response, xstatus, tag); }, 0, pri); _up(); }
+
+    // Perform a WSMAN method call operation.
+    obj.Exec = function Exec(name, method, args, callback, tag, pri, selectors) { obj.wsman.ExecMethod(obj.CompleteName(name), method, args, function (ws, resuri, response, xstatus) { _up(); callback.call(obj, obj, name, obj.CompleteExecResponse(response), xstatus, tag); }, 0, pri, selectors); _up(); }
+	
+    // Perform a WSMAN method call operation.
+    obj.ExecWithXml = function ExecWithXml(name, method, args, callback, tag, pri, selectors) { obj.wsman.ExecMethodXml(obj.CompleteName(name), method, execArgumentsToXml(args), function (ws, resuri, response, xstatus) { _up(); callback.call(obj, obj, name, obj.CompleteExecResponse(response), xstatus, tag); }, 0, pri, selectors); _up(); }
+	
+    // Perform a WSMAN "ENUMERATE" operation.
+    obj.Enum = function Enum(name, callback, tag, pri) {
+        if (obj.ActiveEnumsCount < obj.MaxActiveEnumsCount) {
+            obj.ActiveEnumsCount++; obj.wsman.ExecEnum(obj.CompleteName(name), function (ws, resuri, response, xstatus, tag0) { _up(); _EnumStartSink(name, response, callback, resuri, xstatus, tag0); }, tag, pri);
+        } else {
+            obj.PendingEnums.push([name, callback, tag, pri]);
+        }
+        _up();
+    }
+
+    // Private method
+    function _EnumStartSink(name, response, callback, resuri, status, tag, pri) {
+        if (status != 200) { callback.call(obj, obj, name, null, status, tag); _EnumDoNext(1); return; }
+        if (response == null || response.Header["Method"] != "EnumerateResponse" || !response.Body["EnumerationContext"]) { callback.call(obj, obj, name, null, 603, tag); _EnumDoNext(1); return; }
+        var enumctx = response.Body["EnumerationContext"];
+        obj.wsman.ExecPull(resuri, enumctx, function (ws, resuri, response, xstatus) { _EnumContinueSink(name, response, callback, resuri, [], xstatus, tag, pri); });
+    }
+
+    // Private method
+    function _EnumContinueSink(name, response, callback, resuri, items, status, tag, pri) {
+        if (status != 200) { callback.call(obj, obj, name, null, status, tag); _EnumDoNext(1); return; }
+        if (response == null || response.Header["Method"] != "PullResponse") { callback.call(obj, obj, name, null, 604, tag); _EnumDoNext(1); return; }
+        for (var i in response.Body["Items"]) {
+            if (response.Body["Items"][i] instanceof Array) {
+                for (var j in response.Body["Items"][i]) { items.push(response.Body["Items"][i][j]); }
+            } else {
+                items.push(response.Body["Items"][i]);
+            }
+        }
+        if (response.Body["EnumerationContext"]) {
+            var enumctx = response.Body["EnumerationContext"];
+            obj.wsman.ExecPull(resuri, enumctx, function (ws, resuri, response, xstatus) { _EnumContinueSink(name, response, callback, resuri, items, xstatus, tag, 1); });
+        } else {
+            _EnumDoNext(1);
+            callback.call(obj, obj, name, items, status, tag);
+            _up();
+        }
+    }
+
+    // Private method
+    function _EnumDoNext(dec) {
+        obj.ActiveEnumsCount -= dec;
+        if (obj.ActiveEnumsCount >= obj.MaxActiveEnumsCount || obj.PendingEnums.length == 0) return;
+        var x = obj.PendingEnums.shift();
+        obj.Enum(x[0], x[1], x[2]);
+        _EnumDoNext(0);
+    }
+
+    // Perform a batch of WSMAN "ENUM" operations.
+    obj.BatchEnum = function (batchname, names, callback, tag, continueOnError, pri) {
+        obj.PendingBatchOperations += (names.length * 2);
+        _BatchNextEnum(batchname, Clone(names), callback, tag, {}, continueOnError, pri); _up();
+    }
+
+    function Clone(v) { return JSON.parse(JSON.stringify(v)); }
+
+    // Request each enum in the batch, stopping if something does not return status 200
+    function _BatchNextEnum(batchname, names, callback, tag, results, continueOnError, pri) {
+        obj.PendingBatchOperations -= 2;
+        var n = names.shift(), f = obj.Enum;
+        if (n[0] == '*') { f = obj.Get; n = n.substring(1); } // If the name starts with a star, do a GET instead of an ENUM. This will reduce round trips.
+        //console.log((f == obj.Get?'Get ':'Enum ') + n);
+        // Perform a GET/ENUM action
+        f(n, function (stack, name, responses, status, tag0) {
+            tag0[2][name] = { response: (responses==null?null:responses.Body), responses: responses, status: status };
+            if (tag0[1].length == 0 || status == 401 || (continueOnError != true && status != 200 && status != 400)) { obj.PendingBatchOperations -= (names.length * 2); _up(); callback.call(obj, obj, batchname, tag0[2], status, tag); }
+            else { _up(); _BatchNextEnum(batchname, names, callback, tag, tag0[2], pri); }
+        }, [batchname, names, results], pri);
+        _up();
+    }
+
+    // Perform a batch of WSMAN "GET" operations.
+    obj.BatchGet = function (batchname, names, callback, tag, pri) {
+        _FetchNext({ name: batchname, names: names, callback: callback, current: 0, responses: {}, tag: tag, pri: pri }); _up();
+    }
+
+    // Private method
+    function _FetchNext(batch) {
+        if (batch.names.length <= batch.current) {
+            batch.callback.call(obj, obj, batch.name, batch.responses, 200, batch.tag);
+        } else {
+            obj.wsman.ExecGet(obj.CompleteName(batch.names[batch.current]), function (ws, resuri, response, xstatus) { _Fetched(batch, response, xstatus); }, batch.pri);
+            batch.current++;
+        }
+        _up();
+    }
+
+    // Private method
+    function _Fetched(batch, response, status) {
+        if (response == null || status != 200) {
+            batch.callback.call(obj, obj, batch.name, null, status, batch.tag);
+        } else {
+            batch.responses[response.Header["Method"]] = response;
+            _FetchNext(batch);
+        }
+    }
+
+    // Private method
+    obj.CompleteName = function(name) {
+        if (name.indexOf("AMT_") == 0) return obj.pfx[0] + name;
+        if (name.indexOf("CIM_") == 0) return obj.pfx[1] + name;
+        if (name.indexOf("IPS_") == 0) return obj.pfx[2] + name;
+    }
+
+    obj.CompleteExecResponse = function (resp) {
+        if (resp && resp != null && resp.Body && (resp.Body["ReturnValue"] != undefined)) { resp.Body.ReturnValueStr = obj.AmtStatusToStr(resp.Body["ReturnValue"]); }
+        return resp;
+    }
+
+    obj.RequestPowerStateChange = function (PowerState, callback_func) {
+        obj.CIM_PowerManagementService_RequestPowerStateChange(PowerState, "<Address xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\"><ResourceURI xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem</ResourceURI><SelectorSet xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\"><Selector Name=\"CreationClassName\">CIM_ComputerSystem</Selector><Selector Name=\"Name\">ManagedSystem</Selector></SelectorSet></ReferenceParameters>", null, null, callback_func);
+    }
+
+    obj.SetBootConfigRole = function (Role, callback_func) {
+        obj.CIM_BootService_SetBootConfigRole("<Address xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\"><ResourceURI xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_BootConfigSetting</ResourceURI><SelectorSet xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\"><Selector Name=\"InstanceID\">Intel(r) AMT: Boot Configuration 0</Selector></SelectorSet></ReferenceParameters>", Role, callback_func);
+    }
+
+    // Cancel all pending queries with given status
+    obj.CancelAllQueries = function (s) {
+        obj.wsman.CancelAllQueries(s);
+    }
+
+    // Auto generated methods
+    obj.AMT_AgentPresenceWatchdog_RegisterAgent = function (callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "RegisterAgent", {}, callback_func, tag, pri, selectors); }
+    obj.AMT_AgentPresenceWatchdog_AssertPresence = function (SequenceNumber, callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertPresence", { "SequenceNumber": SequenceNumber }, callback_func, tag, pri, selectors); }
+    obj.AMT_AgentPresenceWatchdog_AssertShutdown = function (SequenceNumber, callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertShutdown", { "SequenceNumber": SequenceNumber }, callback_func, tag, pri, selectors); }
+    obj.AMT_AgentPresenceWatchdog_AddAction = function (OldState, NewState, EventOnTransition, ActionSd, ActionEac, callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "AddAction", { "OldState": OldState, "NewState": NewState, "EventOnTransition": EventOnTransition, "ActionSd": ActionSd, "ActionEac": ActionEac }, callback_func, tag, pri, selectors); }
+    obj.AMT_AgentPresenceWatchdog_DeleteAllActions = function (callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "DeleteAllActions", {}, callback_func, tag, pri, selectors); }
+    obj.AMT_AgentPresenceWatchdogAction_GetActionEac = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdogAction", "GetActionEac", {}, callback_func); }
+    obj.AMT_AgentPresenceWatchdogVA_RegisterAgent = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "RegisterAgent", {}, callback_func); }
+    obj.AMT_AgentPresenceWatchdogVA_AssertPresence = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "AssertPresence", { "SequenceNumber": SequenceNumber }, callback_func); }
+    obj.AMT_AgentPresenceWatchdogVA_AssertShutdown = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "AssertShutdown", { "SequenceNumber": SequenceNumber }, callback_func); }
+    obj.AMT_AgentPresenceWatchdogVA_AddAction = function (OldState, NewState, EventOnTransition, ActionSd, ActionEac, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "AddAction", { "OldState": OldState, "NewState": NewState, "EventOnTransition": EventOnTransition, "ActionSd": ActionSd, "ActionEac": ActionEac }, callback_func); }
+    obj.AMT_AgentPresenceWatchdogVA_DeleteAllActions = function (_method_dummy, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "DeleteAllActions", { "_method_dummy": _method_dummy }, callback_func); }
+    obj.AMT_AlarmClockService_AddAlarm = function AlarmClockService_AddAlarm(alarmInstance, callback_func)
+    {
+        var id = alarmInstance.InstanceID;
+        var nm = alarmInstance.ElementName;
+        var start = alarmInstance.StartTime.Datetime;
+        var interval = alarmInstance.Interval ? alarmInstance.Interval.Datetime : undefined;
+        var doc = alarmInstance.DeleteOnCompletion;
+        var tpl = "<d:AlarmTemplate xmlns:d=\"http://intel.com/wbem/wscim/1/amt-schema/1/AMT_AlarmClockService\" xmlns:s=\"http://intel.com/wbem/wscim/1/ips-schema/1/IPS_AlarmClockOccurrence\"><s:InstanceID>" + id + "</s:InstanceID><s:ElementName>" + nm + "</s:ElementName><s:StartTime><p:Datetime xmlns:p=\"http://schemas.dmtf.org/wbem/wscim/1/common\">" + start + "</p:Datetime></s:StartTime>" + ((interval!=undefined)?("<s:Interval><p:Interval xmlns:p=\"http://schemas.dmtf.org/wbem/wscim/1/common\">" + interval + "</p:Interval></s:Interval>"):"") + "<s:DeleteOnCompletion>" + doc + "</s:DeleteOnCompletion></d:AlarmTemplate>"
+        obj.wsman.ExecMethodXml(obj.CompleteName("AMT_AlarmClockService"), "AddAlarm", tpl, callback_func);
+    };
+    obj.AMT_AuditLog_ClearLog = function (callback_func) { obj.Exec("AMT_AuditLog", "ClearLog", {}, callback_func); }
+    obj.AMT_AuditLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_AuditLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.AMT_AuditLog_ReadRecords = function (StartIndex, callback_func, tag) { obj.Exec("AMT_AuditLog", "ReadRecords", { "StartIndex": StartIndex }, callback_func, tag); }
+    obj.AMT_AuditLog_SetAuditLock = function (LockTimeoutInSeconds, Flag, Handle, callback_func) { obj.Exec("AMT_AuditLog", "SetAuditLock", { "LockTimeoutInSeconds": LockTimeoutInSeconds, "Flag": Flag, "Handle": Handle }, callback_func); }
+    obj.AMT_AuditLog_ExportAuditLogSignature = function (SigningMechanism, callback_func) { obj.Exec("AMT_AuditLog", "ExportAuditLogSignature", { "SigningMechanism": SigningMechanism }, callback_func); }
+    obj.AMT_AuditLog_SetSigningKeyMaterial = function (SigningMechanismType, SigningKey, LengthOfCertificates, Certificates, callback_func) { obj.Exec("AMT_AuditLog", "SetSigningKeyMaterial", { "SigningMechanismType": SigningMechanismType, "SigningKey": SigningKey, "LengthOfCertificates": LengthOfCertificates, "Certificates": Certificates }, callback_func); }
+    obj.AMT_AuditPolicyRule_SetAuditPolicy = function (Enable, AuditedAppID, EventID, PolicyType, callback_func) { obj.Exec("AMT_AuditPolicyRule", "SetAuditPolicy", { "Enable": Enable, "AuditedAppID": AuditedAppID, "EventID": EventID, "PolicyType": PolicyType }, callback_func); }
+    obj.AMT_AuditPolicyRule_SetAuditPolicyBulk = function (Enable, AuditedAppID, EventID, PolicyType, callback_func) { obj.Exec("AMT_AuditPolicyRule", "SetAuditPolicyBulk", { "Enable": Enable, "AuditedAppID": AuditedAppID, "EventID": EventID, "PolicyType": PolicyType }, callback_func); }
+    obj.AMT_AuthorizationService_AddUserAclEntryEx = function (DigestUsername, DigestPassword, KerberosUserSid, AccessPermission, Realms, callback_func) { obj.Exec("AMT_AuthorizationService", "AddUserAclEntryEx", { "DigestUsername": DigestUsername, "DigestPassword": DigestPassword, "KerberosUserSid": KerberosUserSid, "AccessPermission": AccessPermission, "Realms": Realms }, callback_func); }
+    obj.AMT_AuthorizationService_EnumerateUserAclEntries = function (StartIndex, callback_func) { obj.Exec("AMT_AuthorizationService", "EnumerateUserAclEntries", { "StartIndex": StartIndex }, callback_func); }
+    obj.AMT_AuthorizationService_GetUserAclEntryEx = function (Handle, callback_func, tag) { obj.Exec("AMT_AuthorizationService", "GetUserAclEntryEx", { "Handle": Handle }, callback_func, tag); }
+    obj.AMT_AuthorizationService_UpdateUserAclEntryEx = function (Handle, DigestUsername, DigestPassword, KerberosUserSid, AccessPermission, Realms, callback_func) { obj.Exec("AMT_AuthorizationService", "UpdateUserAclEntryEx", { "Handle": Handle, "DigestUsername": DigestUsername, "DigestPassword": DigestPassword, "KerberosUserSid": KerberosUserSid, "AccessPermission": AccessPermission, "Realms": Realms }, callback_func); }
+    obj.AMT_AuthorizationService_RemoveUserAclEntry = function (Handle, callback_func) { obj.Exec("AMT_AuthorizationService", "RemoveUserAclEntry", { "Handle": Handle }, callback_func); }
+    obj.AMT_AuthorizationService_SetAdminAclEntryEx = function (Username, DigestPassword, callback_func) { obj.Exec("AMT_AuthorizationService", "SetAdminAclEntryEx", { "Username": Username, "DigestPassword": DigestPassword }, callback_func); }
+    obj.AMT_AuthorizationService_GetAdminAclEntry = function (callback_func) { obj.Exec("AMT_AuthorizationService", "GetAdminAclEntry", {}, callback_func); }
+    obj.AMT_AuthorizationService_GetAdminAclEntryStatus = function (callback_func) { obj.Exec("AMT_AuthorizationService", "GetAdminAclEntryStatus", {}, callback_func); }
+    obj.AMT_AuthorizationService_GetAdminNetAclEntryStatus = function (callback_func) { obj.Exec("AMT_AuthorizationService", "GetAdminNetAclEntryStatus", {}, callback_func); }
+    obj.AMT_AuthorizationService_SetAclEnabledState = function (Handle, Enabled, callback_func, tag) { obj.Exec("AMT_AuthorizationService", "SetAclEnabledState", { "Handle": Handle, "Enabled": Enabled }, callback_func, tag); }
+    obj.AMT_AuthorizationService_GetAclEnabledState = function (Handle, callback_func, tag) { obj.Exec("AMT_AuthorizationService", "GetAclEnabledState", { "Handle": Handle }, callback_func, tag); }
+    obj.AMT_EndpointAccessControlService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.AMT_EndpointAccessControlService_GetPosture = function (PostureType, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "GetPosture", { "PostureType": PostureType }, callback_func); }
+    obj.AMT_EndpointAccessControlService_GetPostureHash = function (PostureType, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "GetPostureHash", { "PostureType": PostureType }, callback_func); }
+    obj.AMT_EndpointAccessControlService_UpdatePostureState = function (UpdateType, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "UpdatePostureState", { "UpdateType": UpdateType }, callback_func); }
+    obj.AMT_EndpointAccessControlService_GetEacOptions = function (callback_func) { obj.Exec("AMT_EndpointAccessControlService", "GetEacOptions", {}, callback_func); }
+    obj.AMT_EndpointAccessControlService_SetEacOptions = function (EacVendors, PostureHashAlgorithm, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "SetEacOptions", { "EacVendors": EacVendors, "PostureHashAlgorithm": PostureHashAlgorithm }, callback_func); }
+    obj.AMT_EnvironmentDetectionSettingData_SetSystemDefensePolicy = function (Policy, callback_func) { obj.Exec("AMT_EnvironmentDetectionSettingData", "SetSystemDefensePolicy", { "Policy": Policy }, callback_func); }
+    obj.AMT_EnvironmentDetectionSettingData_EnableVpnRouting = function (Enable, callback_func) { obj.Exec("AMT_EnvironmentDetectionSettingData", "EnableVpnRouting", { "Enable": Enable }, callback_func); }
+    obj.AMT_EthernetPortSettings_SetLinkPreference = function (LinkPreference, Timeout, callback_func) { obj.Exec("AMT_EthernetPortSettings", "SetLinkPreference", { "LinkPreference": LinkPreference, "Timeout": Timeout }, callback_func); }
+    obj.AMT_HeuristicPacketFilterStatistics_ResetSelectedStats = function (SelectedStatistics, callback_func) { obj.Exec("AMT_HeuristicPacketFilterStatistics", "ResetSelectedStats", { "SelectedStatistics": SelectedStatistics }, callback_func); }
+    obj.AMT_KerberosSettingData_GetCredentialCacheState = function (callback_func) { obj.Exec("AMT_KerberosSettingData", "GetCredentialCacheState", {}, callback_func); }
+    obj.AMT_KerberosSettingData_SetCredentialCacheState = function (Enable, callback_func) { obj.Exec("AMT_KerberosSettingData", "SetCredentialCacheState", { "Enable": Enable }, callback_func); }
+    obj.AMT_MessageLog_CancelIteration = function (IterationIdentifier, callback_func) { obj.Exec("AMT_MessageLog", "CancelIteration", { "IterationIdentifier": IterationIdentifier }, callback_func); }
+    obj.AMT_MessageLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_MessageLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.AMT_MessageLog_ClearLog = function (callback_func) { obj.Exec("AMT_MessageLog", "ClearLog", { }, callback_func); }
+    obj.AMT_MessageLog_GetRecords = function (IterationIdentifier, MaxReadRecords, callback_func, tag) { obj.Exec("AMT_MessageLog", "GetRecords", { "IterationIdentifier": IterationIdentifier, "MaxReadRecords": MaxReadRecords }, callback_func, tag); }
+    obj.AMT_MessageLog_GetRecord = function (IterationIdentifier, PositionToNext, callback_func) { obj.Exec("AMT_MessageLog", "GetRecord", { "IterationIdentifier": IterationIdentifier, "PositionToNext": PositionToNext }, callback_func); }
+    obj.AMT_MessageLog_PositionAtRecord = function (IterationIdentifier, MoveAbsolute, RecordNumber, callback_func) { obj.Exec("AMT_MessageLog", "PositionAtRecord", { "IterationIdentifier": IterationIdentifier, "MoveAbsolute": MoveAbsolute, "RecordNumber": RecordNumber }, callback_func); }
+    obj.AMT_MessageLog_PositionToFirstRecord = function (callback_func, tag) { obj.Exec("AMT_MessageLog", "PositionToFirstRecord", {}, callback_func, tag); }
+    obj.AMT_MessageLog_FreezeLog = function (Freeze, callback_func) { obj.Exec("AMT_MessageLog", "FreezeLog", { "Freeze": Freeze }, callback_func); }
+    obj.AMT_PublicKeyManagementService_AddCRL = function (Url, SerialNumbers, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddCRL", { "Url": Url, "SerialNumbers": SerialNumbers }, callback_func); }
+    obj.AMT_PublicKeyManagementService_ResetCRLList = function (_method_dummy, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "ResetCRLList", { "_method_dummy": _method_dummy }, callback_func); }
+    obj.AMT_PublicKeyManagementService_AddCertificate = function (CertificateBlob, callback_func, tag) { obj.Exec("AMT_PublicKeyManagementService", "AddCertificate", { "CertificateBlob": CertificateBlob }, callback_func, tag); }
+    obj.AMT_PublicKeyManagementService_AddTrustedRootCertificate = function (CertificateBlob, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddTrustedRootCertificate", { "CertificateBlob": CertificateBlob }, callback_func); }
+    obj.AMT_PublicKeyManagementService_AddKey = function (KeyBlob, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddKey", { "KeyBlob": KeyBlob }, callback_func); }
+    obj.AMT_PublicKeyManagementService_GeneratePKCS10Request = function (KeyPair, DNName, Usage, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "GeneratePKCS10Request", { "KeyPair": KeyPair, "DNName": DNName, "Usage": Usage }, callback_func); }
+    obj.AMT_PublicKeyManagementService_GeneratePKCS10RequestEx = function (KeyPair, SigningAlgorithm, NullSignedCertificateRequest, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "GeneratePKCS10RequestEx", { "KeyPair": KeyPair, "SigningAlgorithm": SigningAlgorithm, "NullSignedCertificateRequest": NullSignedCertificateRequest }, callback_func); }
+    obj.AMT_PublicKeyManagementService_GenerateKeyPair = function (KeyAlgorithm, KeyLength, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "GenerateKeyPair", { "KeyAlgorithm": KeyAlgorithm, "KeyLength": KeyLength }, callback_func); }
+    obj.AMT_RedirectionService_RequestStateChange = function (RequestedState, callback_func) { obj.Exec("AMT_RedirectionService", "RequestStateChange", { "RequestedState": RequestedState }, callback_func); }
+    obj.AMT_RedirectionService_TerminateSession = function (SessionType, callback_func) { obj.Exec("AMT_RedirectionService", "TerminateSession", { "SessionType": SessionType }, callback_func); }
+    obj.AMT_RemoteAccessService_AddMpServer = function (AccessInfo, InfoFormat, Port, AuthMethod, Certificate, Username, Password, CN, callback_func) { obj.Exec("AMT_RemoteAccessService", "AddMpServer", { "AccessInfo": AccessInfo, "InfoFormat": InfoFormat, "Port": Port, "AuthMethod": AuthMethod, "Certificate": Certificate, "Username": Username, "Password": Password, "CN": CN }, callback_func); }
+    obj.AMT_RemoteAccessService_AddRemoteAccessPolicyRule = function (Trigger, TunnelLifeTime, ExtendedData, MpServer, callback_func) { obj.Exec("AMT_RemoteAccessService", "AddRemoteAccessPolicyRule", { "Trigger": Trigger, "TunnelLifeTime": TunnelLifeTime, "ExtendedData": ExtendedData, "MpServer": MpServer }, callback_func); }
+    obj.AMT_RemoteAccessService_CloseRemoteAccessConnection = function (_method_dummy, callback_func) { obj.Exec("AMT_RemoteAccessService", "CloseRemoteAccessConnection", { "_method_dummy": _method_dummy }, callback_func); }
+    obj.AMT_SetupAndConfigurationService_CommitChanges = function (_method_dummy, callback_func, tag) { obj.Exec("AMT_SetupAndConfigurationService", "CommitChanges", { "_method_dummy": _method_dummy }, callback_func, tag); }
+    obj.AMT_SetupAndConfigurationService_Unprovision = function (ProvisioningMode, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "Unprovision", { "ProvisioningMode": ProvisioningMode }, callback_func); }
+    obj.AMT_SetupAndConfigurationService_PartialUnprovision = function (_method_dummy, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "PartialUnprovision", { "_method_dummy": _method_dummy }, callback_func); }
+    obj.AMT_SetupAndConfigurationService_ResetFlashWearOutProtection = function (_method_dummy, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "ResetFlashWearOutProtection", { "_method_dummy": _method_dummy }, callback_func); }
+    obj.AMT_SetupAndConfigurationService_ExtendProvisioningPeriod = function (Duration, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "ExtendProvisioningPeriod", { "Duration": Duration }, callback_func); }
+    obj.AMT_SetupAndConfigurationService_SetMEBxPassword = function (Password, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "SetMEBxPassword", { "Password": Password }, callback_func); }
+    obj.AMT_SetupAndConfigurationService_SetTLSPSK = function (PID, PPS, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "SetTLSPSK", { "PID": PID, "PPS": PPS }, callback_func); }
+    obj.AMT_SetupAndConfigurationService_GetProvisioningAuditRecord = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetProvisioningAuditRecord", {}, callback_func); }
+    obj.AMT_SetupAndConfigurationService_GetUuid = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetUuid", {}, callback_func); }
+    obj.AMT_SetupAndConfigurationService_GetUnprovisionBlockingComponents = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetUnprovisionBlockingComponents", {}, callback_func); }
+    obj.AMT_SetupAndConfigurationService_GetProvisioningAuditRecordV2 = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetProvisioningAuditRecordV2", {}, callback_func); }
+    obj.AMT_SystemDefensePolicy_GetTimeout = function (callback_func) { obj.Exec("AMT_SystemDefensePolicy", "GetTimeout", {}, callback_func); }
+    obj.AMT_SystemDefensePolicy_SetTimeout = function (Timeout, callback_func) { obj.Exec("AMT_SystemDefensePolicy", "SetTimeout", { "Timeout": Timeout }, callback_func); }
+    obj.AMT_SystemDefensePolicy_UpdateStatistics = function (NetworkInterface, ResetOnRead, callback_func, tag, pri, selectors) { obj.Exec("AMT_SystemDefensePolicy", "UpdateStatistics", { "NetworkInterface": NetworkInterface, "ResetOnRead": ResetOnRead }, callback_func, tag, pri, selectors); }
+    obj.AMT_SystemPowerScheme_SetPowerScheme = function (callback_func, schemeInstanceId, tag) { obj.Exec("AMT_SystemPowerScheme", "SetPowerScheme", {}, callback_func, tag, 0, { "InstanceID": schemeInstanceId }); }
+    obj.AMT_TimeSynchronizationService_GetLowAccuracyTimeSynch = function (callback_func, tag) { obj.Exec("AMT_TimeSynchronizationService", "GetLowAccuracyTimeSynch", {}, callback_func, tag); }
+    obj.AMT_TimeSynchronizationService_SetHighAccuracyTimeSynch = function (Ta0, Tm1, Tm2, callback_func, tag) { obj.Exec("AMT_TimeSynchronizationService", "SetHighAccuracyTimeSynch", { "Ta0": Ta0, "Tm1": Tm1, "Tm2": Tm2 }, callback_func, tag); }
+    obj.AMT_TLSCredentialContext_Create = function AMT_TLSCredentialContext_Create(ElementInContext, ElementProvidingContext, callback_func, tag) { obj.Create("AMT_TLSCredentialContext", { "ElementInContext": ElementInContext, "ElementProvidingContext": ElementProvidingContext }, callback_func, tag); }
+    obj.AMT_UserInitiatedConnectionService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_UserInitiatedConnectionService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.AMT_WebUIService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func, tag) { obj.Exec("AMT_WebUIService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func, tag); }
+    obj.AMT_WiFiPortConfigurationService_AddWiFiSettings = function (WiFiEndpoint, WiFiEndpointSettingsInput, IEEE8021xSettingsInput, ClientCredential, CACredential, callback_func) { obj.ExecWithXml("AMT_WiFiPortConfigurationService", "AddWiFiSettings", { "WiFiEndpoint": WiFiEndpoint, "WiFiEndpointSettingsInput": WiFiEndpointSettingsInput, "IEEE8021xSettingsInput": IEEE8021xSettingsInput, "ClientCredential": ClientCredential, "CACredential": CACredential }, callback_func); }
+    obj.AMT_WiFiPortConfigurationService_UpdateWiFiSettings = function (WiFiEndpointSettings, WiFiEndpointSettingsInput, IEEE8021xSettingsInput, ClientCredential, CACredential, callback_func) { obj.ExecWithXml("AMT_WiFiPortConfigurationService", "UpdateWiFiSettings", { "WiFiEndpointSettings": WiFiEndpointSettings, "WiFiEndpointSettingsInput": WiFiEndpointSettingsInput, "IEEE8021xSettingsInput": IEEE8021xSettingsInput, "ClientCredential": ClientCredential, "CACredential": CACredential }, callback_func); }
+    obj.AMT_WiFiPortConfigurationService_DeleteAllITProfiles = function (_method_dummy, callback_func) { obj.Exec("AMT_WiFiPortConfigurationService", "DeleteAllITProfiles", { "_method_dummy": _method_dummy }, callback_func); }
+    obj.AMT_WiFiPortConfigurationService_DeleteAllUserProfiles = function (_method_dummy, callback_func) { obj.Exec("AMT_WiFiPortConfigurationService", "DeleteAllUserProfiles", { "_method_dummy": _method_dummy }, callback_func); }
+    obj.CIM_Account_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Account", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.CIM_AccountManagementService_CreateAccount = function (System, AccountTemplate, callback_func) { obj.Exec("CIM_AccountManagementService", "CreateAccount", { "System": System, "AccountTemplate": AccountTemplate }, callback_func); }
+    obj.CIM_BootConfigSetting_ChangeBootOrder = function (Source, callback_func) { obj.Exec("CIM_BootConfigSetting", "ChangeBootOrder", { "Source": Source }, callback_func); }
+    obj.CIM_BootService_SetBootConfigRole = function (BootConfigSetting, Role, callback_func) { obj.Exec("CIM_BootService", "SetBootConfigRole", { "BootConfigSetting": BootConfigSetting, "Role": Role }, callback_func, 0, 1); }
+    obj.CIM_BootService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func, tag) { obj.Exec('CIM_BootService', 'RequestStateChange', { 'RequestedState': RequestedState, 'TimeoutPeriod': TimeoutPeriod }, callback_func, tag, 1); }
+    obj.CIM_Card_ConnectorPower = function (Connector, PoweredOn, callback_func) { obj.Exec("CIM_Card", "ConnectorPower", { "Connector": Connector, "PoweredOn": PoweredOn }, callback_func); }
+    obj.CIM_Card_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_Card", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
+    obj.CIM_Chassis_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_Chassis", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
+    obj.CIM_Fan_SetSpeed = function (DesiredSpeed, callback_func) { obj.Exec("CIM_Fan", "SetSpeed", { "DesiredSpeed": DesiredSpeed }, callback_func); }
+    obj.CIM_KVMRedirectionSAP_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_KVMRedirectionSAP", "RequestStateChange", { "RequestedState": RequestedState/*, "TimeoutPeriod": TimeoutPeriod */}, callback_func); }
+    obj.CIM_MediaAccessDevice_LockMedia = function (Lock, callback_func) { obj.Exec("CIM_MediaAccessDevice", "LockMedia", { "Lock": Lock }, callback_func); }
+    obj.CIM_MediaAccessDevice_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_MediaAccessDevice", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
+    obj.CIM_MediaAccessDevice_Reset = function (callback_func) { obj.Exec("CIM_MediaAccessDevice", "Reset", {}, callback_func); }
+    obj.CIM_MediaAccessDevice_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_MediaAccessDevice", "EnableDevice", { "Enabled": Enabled }, callback_func); }
+    obj.CIM_MediaAccessDevice_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_MediaAccessDevice", "OnlineDevice", { "Online": Online }, callback_func); }
+    obj.CIM_MediaAccessDevice_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_MediaAccessDevice", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
+    obj.CIM_MediaAccessDevice_SaveProperties = function (callback_func) { obj.Exec("CIM_MediaAccessDevice", "SaveProperties", {}, callback_func); }
+    obj.CIM_MediaAccessDevice_RestoreProperties = function (callback_func) { obj.Exec("CIM_MediaAccessDevice", "RestoreProperties", {}, callback_func); }
+    obj.CIM_MediaAccessDevice_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_MediaAccessDevice", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.CIM_PhysicalFrame_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_PhysicalFrame", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
+    obj.CIM_PhysicalPackage_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_PhysicalPackage", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
+    obj.CIM_PowerManagementService_RequestPowerStateChange = function (PowerState, ManagedElement, Time, TimeoutPeriod, callback_func) { obj.Exec("CIM_PowerManagementService", "RequestPowerStateChange", { "PowerState": PowerState, "ManagedElement": ManagedElement, "Time": Time, "TimeoutPeriod": TimeoutPeriod }, callback_func, 0, 1); }
+    obj.CIM_PowerSupply_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_PowerSupply", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
+    obj.CIM_PowerSupply_Reset = function (callback_func) { obj.Exec("CIM_PowerSupply", "Reset", {}, callback_func); }
+    obj.CIM_PowerSupply_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_PowerSupply", "EnableDevice", { "Enabled": Enabled }, callback_func); }
+    obj.CIM_PowerSupply_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_PowerSupply", "OnlineDevice", { "Online": Online }, callback_func); }
+    obj.CIM_PowerSupply_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_PowerSupply", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
+    obj.CIM_PowerSupply_SaveProperties = function (callback_func) { obj.Exec("CIM_PowerSupply", "SaveProperties", {}, callback_func); }
+    obj.CIM_PowerSupply_RestoreProperties = function (callback_func) { obj.Exec("CIM_PowerSupply", "RestoreProperties", {}, callback_func); }
+    obj.CIM_PowerSupply_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_PowerSupply", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.CIM_Processor_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_Processor", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
+    obj.CIM_Processor_Reset = function (callback_func) { obj.Exec("CIM_Processor", "Reset", {}, callback_func); }
+    obj.CIM_Processor_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_Processor", "EnableDevice", { "Enabled": Enabled }, callback_func); }
+    obj.CIM_Processor_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_Processor", "OnlineDevice", { "Online": Online }, callback_func); }
+    obj.CIM_Processor_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_Processor", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
+    obj.CIM_Processor_SaveProperties = function (callback_func) { obj.Exec("CIM_Processor", "SaveProperties", {}, callback_func); }
+    obj.CIM_Processor_RestoreProperties = function (callback_func) { obj.Exec("CIM_Processor", "RestoreProperties", {}, callback_func); }
+    obj.CIM_Processor_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Processor", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.CIM_RecordLog_ClearLog = function (callback_func) { obj.Exec("CIM_RecordLog", "ClearLog", {}, callback_func); }
+    obj.CIM_RecordLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_RecordLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.CIM_RedirectionService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_RedirectionService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.CIM_Sensor_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_Sensor", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
+    obj.CIM_Sensor_Reset = function (callback_func) { obj.Exec("CIM_Sensor", "Reset", {}, callback_func); }
+    obj.CIM_Sensor_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_Sensor", "EnableDevice", { "Enabled": Enabled }, callback_func); }
+    obj.CIM_Sensor_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_Sensor", "OnlineDevice", { "Online": Online }, callback_func); }
+    obj.CIM_Sensor_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_Sensor", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
+    obj.CIM_Sensor_SaveProperties = function (callback_func) { obj.Exec("CIM_Sensor", "SaveProperties", {}, callback_func); }
+    obj.CIM_Sensor_RestoreProperties = function (callback_func) { obj.Exec("CIM_Sensor", "RestoreProperties", {}, callback_func); }
+    obj.CIM_Sensor_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Sensor", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.CIM_StatisticalData_ResetSelectedStats = function (SelectedStatistics, callback_func) { obj.Exec("CIM_StatisticalData", "ResetSelectedStats", { "SelectedStatistics": SelectedStatistics }, callback_func); }
+    obj.CIM_Watchdog_KeepAlive = function (callback_func) { obj.Exec("CIM_Watchdog", "KeepAlive", {}, callback_func); }
+    obj.CIM_Watchdog_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_Watchdog", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
+    obj.CIM_Watchdog_Reset = function (callback_func) { obj.Exec("CIM_Watchdog", "Reset", {}, callback_func); }
+    obj.CIM_Watchdog_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_Watchdog", "EnableDevice", { "Enabled": Enabled }, callback_func); }
+    obj.CIM_Watchdog_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_Watchdog", "OnlineDevice", { "Online": Online }, callback_func); }
+    obj.CIM_Watchdog_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_Watchdog", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
+    obj.CIM_Watchdog_SaveProperties = function (callback_func) { obj.Exec("CIM_Watchdog", "SaveProperties", {}, callback_func); }
+    obj.CIM_Watchdog_RestoreProperties = function (callback_func) { obj.Exec("CIM_Watchdog", "RestoreProperties", {}, callback_func); }
+    obj.CIM_Watchdog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Watchdog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.CIM_WiFiPort_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_WiFiPort", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
+    obj.CIM_WiFiPort_Reset = function (callback_func) { obj.Exec("CIM_WiFiPort", "Reset", {}, callback_func); }
+    obj.CIM_WiFiPort_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_WiFiPort", "EnableDevice", { "Enabled": Enabled }, callback_func); }
+    obj.CIM_WiFiPort_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_WiFiPort", "OnlineDevice", { "Online": Online }, callback_func); }
+    obj.CIM_WiFiPort_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_WiFiPort", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
+    obj.CIM_WiFiPort_SaveProperties = function (callback_func) { obj.Exec("CIM_WiFiPort", "SaveProperties", {}, callback_func); }
+    obj.CIM_WiFiPort_RestoreProperties = function (callback_func) { obj.Exec("CIM_WiFiPort", "RestoreProperties", {}, callback_func); }
+    obj.CIM_WiFiPort_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_WiFiPort", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.IPS_HostBasedSetupService_Setup = function (NetAdminPassEncryptionType, NetworkAdminPassword, McNonce, Certificate, SigningAlgorithm, DigitalSignature, callback_func) { obj.Exec("IPS_HostBasedSetupService", "Setup", { "NetAdminPassEncryptionType": NetAdminPassEncryptionType, "NetworkAdminPassword": NetworkAdminPassword, "McNonce": McNonce, "Certificate": Certificate, "SigningAlgorithm": SigningAlgorithm, "DigitalSignature": DigitalSignature }, callback_func); }
+    obj.IPS_HostBasedSetupService_AddNextCertInChain = function (NextCertificate, IsLeafCertificate, IsRootCertificate, callback_func) { obj.Exec("IPS_HostBasedSetupService", "AddNextCertInChain", { "NextCertificate": NextCertificate, "IsLeafCertificate": IsLeafCertificate, "IsRootCertificate": IsRootCertificate }, callback_func); }
+    obj.IPS_HostBasedSetupService_AdminSetup = function (NetAdminPassEncryptionType, NetworkAdminPassword, McNonce, SigningAlgorithm, DigitalSignature, callback_func) { obj.Exec("IPS_HostBasedSetupService", "AdminSetup", { "NetAdminPassEncryptionType": NetAdminPassEncryptionType, "NetworkAdminPassword": NetworkAdminPassword, "McNonce": McNonce, "SigningAlgorithm": SigningAlgorithm, "DigitalSignature": DigitalSignature }, callback_func); }
+    obj.IPS_HostBasedSetupService_UpgradeClientToAdmin = function (McNonce, SigningAlgorithm, DigitalSignature, callback_func) { obj.Exec("IPS_HostBasedSetupService", "UpgradeClientToAdmin", { "McNonce": McNonce, "SigningAlgorithm": SigningAlgorithm, "DigitalSignature": DigitalSignature }, callback_func); }
+    obj.IPS_HostBasedSetupService_DisableClientControlMode = function (_method_dummy, callback_func) { obj.Exec("IPS_HostBasedSetupService", "DisableClientControlMode", { "_method_dummy": _method_dummy }, callback_func); }
+    obj.IPS_KVMRedirectionSettingData_TerminateSession = function (callback_func) { obj.Exec("IPS_KVMRedirectionSettingData", "TerminateSession", {}, callback_func); }
+    obj.IPS_KVMRedirectionSettingData_DataChannelRead = function (callback_func) { obj.Exec("IPS_KVMRedirectionSettingData", "DataChannelRead", {}, callback_func); }
+    obj.IPS_KVMRedirectionSettingData_DataChannelWrite = function (Data, callback_func) { obj.Exec("IPS_KVMRedirectionSettingData", "DataChannelWrite", { "DataMessage": Data }, callback_func); }
+    obj.IPS_OptInService_StartOptIn = function (callback_func) { obj.Exec("IPS_OptInService", "StartOptIn", {}, callback_func); }
+    obj.IPS_OptInService_CancelOptIn = function (callback_func) { obj.Exec("IPS_OptInService", "CancelOptIn", {}, callback_func); }
+    obj.IPS_OptInService_SendOptInCode = function (OptInCode, callback_func) { obj.Exec("IPS_OptInService", "SendOptInCode", { "OptInCode": OptInCode }, callback_func); }
+    obj.IPS_OptInService_StartService = function (callback_func) { obj.Exec("IPS_OptInService", "StartService", {}, callback_func); }
+    obj.IPS_OptInService_StopService = function (callback_func) { obj.Exec("IPS_OptInService", "StopService", {}, callback_func); }
+    obj.IPS_OptInService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("IPS_OptInService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.IPS_ProvisioningRecordLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("IPS_ProvisioningRecordLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+    obj.IPS_ProvisioningRecordLog_ClearLog = function (_method_dummy, callback_func) { obj.Exec("IPS_ProvisioningRecordLog", "ClearLog", { "_method_dummy": _method_dummy }, callback_func); }
+    obj.IPS_SecIOService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("IPS_SecIOService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
+
+    obj.AmtStatusToStr = function (code) { if (obj.AmtStatusCodes[code]) return obj.AmtStatusCodes[code]; else return "UNKNOWN_ERROR" }
+    obj.AmtStatusCodes = {
+        0x0000: "SUCCESS",
+        0x0001: "INTERNAL_ERROR",
+        0x0002: "NOT_READY",
+        0x0003: "INVALID_PT_MODE",
+        0x0004: "INVALID_MESSAGE_LENGTH",
+        0x0005: "TABLE_FINGERPRINT_NOT_AVAILABLE",
+        0x0006: "INTEGRITY_CHECK_FAILED",
+        0x0007: "UNSUPPORTED_ISVS_VERSION",
+        0x0008: "APPLICATION_NOT_REGISTERED",
+        0x0009: "INVALID_REGISTRATION_DATA",
+        0x000A: "APPLICATION_DOES_NOT_EXIST",
+        0x000B: "NOT_ENOUGH_STORAGE",
+        0x000C: "INVALID_NAME",
+        0x000D: "BLOCK_DOES_NOT_EXIST",
+        0x000E: "INVALID_BYTE_OFFSET",
+        0x000F: "INVALID_BYTE_COUNT",
+        0x0010: "NOT_PERMITTED",
+        0x0011: "NOT_OWNER",
+        0x0012: "BLOCK_LOCKED_BY_OTHER",
+        0x0013: "BLOCK_NOT_LOCKED",
+        0x0014: "INVALID_GROUP_PERMISSIONS",
+        0x0015: "GROUP_DOES_NOT_EXIST",
+        0x0016: "INVALID_MEMBER_COUNT",
+        0x0017: "MAX_LIMIT_REACHED",
+        0x0018: "INVALID_AUTH_TYPE",
+        0x0019: "AUTHENTICATION_FAILED",
+        0x001A: "INVALID_DHCP_MODE",
+        0x001B: "INVALID_IP_ADDRESS",
+        0x001C: "INVALID_DOMAIN_NAME",
+        0x001D: "UNSUPPORTED_VERSION",
+        0x001E: "REQUEST_UNEXPECTED",
+        0x001F: "INVALID_TABLE_TYPE",
+        0x0020: "INVALID_PROVISIONING_STATE",
+        0x0021: "UNSUPPORTED_OBJECT",
+        0x0022: "INVALID_TIME",
+        0x0023: "INVALID_INDEX",
+        0x0024: "INVALID_PARAMETER",
+        0x0025: "INVALID_NETMASK",
+        0x0026: "FLASH_WRITE_LIMIT_EXCEEDED",
+        0x0027: "INVALID_IMAGE_LENGTH",
+        0x0028: "INVALID_IMAGE_SIGNATURE",
+        0x0029: "PROPOSE_ANOTHER_VERSION",
+        0x002A: "INVALID_PID_FORMAT",
+        0x002B: "INVALID_PPS_FORMAT",
+        0x002C: "BIST_COMMAND_BLOCKED",
+        0x002D: "CONNECTION_FAILED",
+        0x002E: "CONNECTION_TOO_MANY",
+        0x002F: "RNG_GENERATION_IN_PROGRESS",
+        0x0030: "RNG_NOT_READY",
+        0x0031: "CERTIFICATE_NOT_READY",
+        0x0400: "DISABLED_BY_POLICY",
+        0x0800: "NETWORK_IF_ERROR_BASE",
+        0x0801: "UNSUPPORTED_OEM_NUMBER",
+        0x0802: "UNSUPPORTED_BOOT_OPTION",
+        0x0803: "INVALID_COMMAND",
+        0x0804: "INVALID_SPECIAL_COMMAND",
+        0x0805: "INVALID_HANDLE",
+        0x0806: "INVALID_PASSWORD",
+        0x0807: "INVALID_REALM",
+        0x0808: "STORAGE_ACL_ENTRY_IN_USE",
+        0x0809: "DATA_MISSING",
+        0x080A: "DUPLICATE",
+        0x080B: "EVENTLOG_FROZEN",
+        0x080C: "PKI_MISSING_KEYS",
+        0x080D: "PKI_GENERATING_KEYS",
+        0x080E: "INVALID_KEY",
+        0x080F: "INVALID_CERT",
+        0x0810: "CERT_KEY_NOT_MATCH",
+        0x0811: "MAX_KERB_DOMAIN_REACHED",
+        0x0812: "UNSUPPORTED",
+        0x0813: "INVALID_PRIORITY",
+        0x0814: "NOT_FOUND",
+        0x0815: "INVALID_CREDENTIALS",
+        0x0816: "INVALID_PASSPHRASE",
+        0x0818: "NO_ASSOCIATION",
+        0x081B: "AUDIT_FAIL",
+        0x081C: "BLOCKING_COMPONENT",
+        0x0821: "USER_CONSENT_REQUIRED",
+        0x1000: "APP_INTERNAL_ERROR",
+        0x1001: "NOT_INITIALIZED",
+        0x1002: "LIB_VERSION_UNSUPPORTED",
+        0x1003: "INVALID_PARAM",
+        0x1004: "RESOURCES",
+        0x1005: "HARDWARE_ACCESS_ERROR",
+        0x1006: "REQUESTOR_NOT_REGISTERED",
+        0x1007: "NETWORK_ERROR",
+        0x1008: "PARAM_BUFFER_TOO_SHORT",
+        0x1009: "COM_NOT_INITIALIZED_IN_THREAD",
+        0x100A: "URL_REQUIRED"
+    }
+
+    //
+    // Methods used for getting the event log
+    //
+
+    obj.GetMessageLog = function (func, tag) {
+        obj.AMT_MessageLog_PositionToFirstRecord(_GetMessageLog0, [func, tag, []]);
+    }
+    function _GetMessageLog0(stack, name, responses, status, tag) {
+        if (status != 200 || responses.Body["ReturnValue"] != '0') { tag[0](obj, null, tag[2], status); return; }
+        obj.AMT_MessageLog_GetRecords(responses.Body["IterationIdentifier"], 390, _GetMessageLog1, tag);
+    }
+    function _GetMessageLog1(stack, name, responses, status, tag) {
+        if (status != 200 || responses.Body["ReturnValue"] != '0') { tag[0](obj, null, tag[2], status); return; }
+        var i, j, x, e, AmtMessages = tag[2], t = new Date(), TimeStamp, ra = responses.Body["RecordArray"];
+        if (typeof ra === 'string') { ra = [ra]; }
+
+        for (i in ra) {
+            e = Buffer.from(ra[i], 'base64');
+            if (e != null) {
+                TimeStamp = ReadIntX(e, 0);
+                if ((TimeStamp > 0) && (TimeStamp < 0xFFFFFFFF)) {
+                    x = { 'DeviceAddress': e[4], 'EventSensorType': e[5], 'EventType': e[6], 'EventOffset': e[7], 'EventSourceType': e[8], 'EventSeverity': e[9], 'SensorNumber': e[10], 'Entity': e[11], 'EntityInstance': e[12], 'EventData': [], 'Time': new Date((TimeStamp + (t.getTimezoneOffset() * 60)) * 1000) };
+                    for (j = 13; j < 21; j++) { x['EventData'].push(e[j]); }
+                    x['EntityStr'] = _SystemEntityTypes[x['Entity']];
+                    x['Desc'] = _GetEventDetailStr(x['EventSensorType'], x['EventOffset'], x['EventData'], x['Entity']);
+                    if (!x['EntityStr']) x['EntityStr'] = "Unknown";
+                    AmtMessages.push(x);
+                }
+            }
+        }
+
+        if (responses.Body["NoMoreRecords"] != true) { obj.AMT_MessageLog_GetRecords(responses.Body["IterationIdentifier"], 390, _GetMessageLog1, [tag[0], AmtMessages, tag[2]]); } else { tag[0](obj, AmtMessages, tag[2]); }
+    }
+
+    var _EventTrapSourceTypes = "Platform firmware (e.g. BIOS)|SMI handler|ISV system management software|Alert ASIC|IPMI|BIOS vendor|System board set vendor|System integrator|Third party add-in|OSV|NIC|System management card".split('|');
+    var _SystemFirmwareError = "Unspecified.|No system memory is physically installed in the system.|No usable system memory, all installed memory has experienced an unrecoverable failure.|Unrecoverable hard-disk/ATAPI/IDE device failure.|Unrecoverable system-board failure.|Unrecoverable diskette subsystem failure.|Unrecoverable hard-disk controller failure.|Unrecoverable PS/2 or USB keyboard failure.|Removable boot media not found.|Unrecoverable video controller failure.|No video device detected.|Firmware (BIOS) ROM corruption detected.|CPU voltage mismatch (processors that share same supply have mismatched voltage requirements)|CPU speed matching failure".split('|');
+    var _SystemFirmwareProgress = "Unspecified.|Memory initialization.|Starting hard-disk initialization and test|Secondary processor(s) initialization|User authentication|User-initiated system setup|USB resource configuration|PCI resource configuration|Option ROM initialization|Video initialization|Cache initialization|SM Bus initialization|Keyboard controller initialization|Embedded controller/management controller initialization|Docking station attachment|Enabling docking station|Docking station ejection|Disabling docking station|Calling operating system wake-up vector|Starting operating system boot process|Baseboard or motherboard initialization|reserved|Floppy initialization|Keyboard test|Pointing device test|Primary processor initialization".split('|');
+    var _SystemEntityTypes = "Unspecified|Other|Unknown|Processor|Disk|Peripheral|System management module|System board|Memory module|Processor module|Power supply|Add in card|Front panel board|Back panel board|Power system board|Drive backplane|System internal expansion board|Other system board|Processor board|Power unit|Power module|Power management board|Chassis back panel board|System chassis|Sub chassis|Other chassis board|Disk drive bay|Peripheral bay|Device bay|Fan cooling|Cooling unit|Cable interconnect|Memory device|System management software|BIOS|Intel(r) ME|System bus|Group|Intel(r) ME|External environment|Battery|Processing blade|Connectivity switch|Processor/memory module|I/O module|Processor I/O module|Management controller firmware|IPMI channel|PCI bus|PCI express bus|SCSI bus|SATA/SAS bus|Processor front side bus".split('|');
+    obj.RealmNames = "||Redirection|PT Administration|Hardware Asset|Remote Control|Storage|Event Manager|Storage Admin|Agent Presence Local|Agent Presence Remote|Circuit Breaker|Network Time|General Information|Firmware Update|EIT|LocalUN|Endpoint Access Control|Endpoint Access Control Admin|Event Log Reader|Audit Log|ACL Realm|||Local System".split('|');
+    obj.WatchdogCurrentStates = { 1: 'Not Started', 2: 'Stopped', 4: 'Running', 8: 'Expired', 16: 'Suspended' };
+    var _OCRProgressEvents = ["Boot parameters received from CSME", "CSME Boot Option % added successfully", "HTTPS URI name resolved", "HTTPS connected successfully", "HTTPSBoot download is completed", "Attempt to boot", "Exit boot services"];
+    var _OCRErrorEvents = ['', "No network connection available", "Name resolution of URI failed", "Connect to URI failed", "OEM app not found at local URI", "HTTPS TLS Auth failed", "HTTPS Digest Auth failed", "Verified boot failed (bad image)", "HTTPS Boot File not found"];
+    var _OCRSource = { 1: '', 2: "HTTPS", 4: "Local PBA", 8: "WinRE" };
+
+    function _GetEventDetailStr(eventSensorType, eventOffset, eventDataField, entity) {
+        if (eventSensorType == 15) {
+            if (eventDataField[0] == 235) return "Invalid Data";
+            if (eventOffset == 0) {
+                return _SystemFirmwareError[eventDataField[1]];
+            } else if (eventOffset == 3) {
+                if ((eventDataField[0] == 170) && (eventDataField[1] == 48)) {
+                    return format("One Click Recovery: {0}", _OCRErrorEvents[eventDataField[2]]);
+                } else if ((eventDataField[0] == 170) && (eventDataField[1] == 64)) {
+                    if (eventDataField[2] == 1) return "Got an error erasing Device SSD";
+                    if (eventDataField[2] == 2) return "Erasing Device TPM is not supported";
+                    if (eventDataField[2] == 3) return "Reached Max Counter";
+                } else {
+                    return "OEM Specific Firmware Error event";
+                }
+            } else if (eventOffset == 5) {
+                if ((eventDataField[0] == 170) && (eventDataField[1] == 48)) {
+                    if (eventDataField[2] == 1) {
+                        return format("One Click Recovery: CSME Boot Option {0}:{1} added successfully", (eventDataField[3]), _OCRSource[(eventDataField[3])]);
+                    } else if (eventDataField[2] < 7) {
+                        return format("One Click Recovery: {0}", _OCRProgressEvents[eventDataField[2]]);
+                    } else {
+                        return format("One Click Recovery: Unknown progress event {0}", eventDataField[2]);
+                    }
+                } else if ((eventDataField[0] == 170) && (eventDataField[1] == 64)) {
+                    if (eventDataField[2] == 1) {
+                        if (eventDataField[3] == 2) return "Started erasing Device SSD";
+                        if (eventDataField[3] == 3) return "Started erasing Device TPM";
+                        if (eventDataField[3] == 5) return "Started erasing Device BIOS Reload of Golden Config";
+                    }
+                    if (eventDataField[2] == 2) {
+                        if (eventDataField[3] == 2) return "Erasing Device SSD ended successfully";
+                        if (eventDataField[3] == 3) return "Erasing Device TPM ended successfully";
+                        if (eventDataField[3] == 5) return "Erasing Device BIOS Reload of Golden Config ended successfully";
+                    }
+                    if (eventDataField[2] == 3) return "Beginning Platform Erase";
+                    if (eventDataField[2] == 4) return "Clear Reserved Parameters";
+                    if (eventDataField[2] == 5) return "All setting decremented";
+                } else {
+                    return "OEM Specific Firmware Progress event";
+                }
+            } else {
+                return _SystemFirmwareProgress[eventDataField[1]];
+            }
+        }
+
+        if ((eventSensorType == 18) && (eventDataField[0] == 170)) // System watchdog event
+        {
+            return "Agent watchdog " + char2hex(eventDataField[4]) + char2hex(eventDataField[3]) + char2hex(eventDataField[2]) + char2hex(eventDataField[1]) + "-" + char2hex(eventDataField[6]) + char2hex(eventDataField[5]) + "-... changed to " + obj.WatchdogCurrentStates[eventDataField[7]];
+        }
+
+        if ((eventSensorType == 5) && (eventOffset == 0)) // System chassis
+        {
+            return "Case intrusion";
+        }
+
+        if ((eventSensorType == 192) && (eventOffset == 0) && (eventDataField[0] == 170) && (eventDataField[1] == 48))
+        {
+            if (eventDataField[2] == 0) return "A remote Serial Over LAN session was established.";
+            if (eventDataField[2] == 1) return "Remote Serial Over LAN session finished. User control was restored.";
+            if (eventDataField[2] == 2) return "A remote IDE-Redirection session was established.";
+            if (eventDataField[2] == 3) return "Remote IDE-Redirection session finished. User control was restored.";
+        }
+
+        if (eventSensorType == 36)
+        {
+            var handle = (eventDataField[1] << 24) + (eventDataField[2] << 16) + (eventDataField[3] << 8) + eventDataField[4];
+            var nic = '#' + eventDataField[0];
+            if (eventDataField[0] == 0xAA) nic = "wired"; // TODO: Add wireless *****
+            //if (eventDataField[0] == 0xAA) nic = "wireless";
+
+            if (handle == 4294967293) { return "All received packet filter was matched on " + nic + " interface."; }
+            if (handle == 4294967292) { return "All outbound packet filter was matched on " + nic + " interface."; }
+            if (handle == 4294967290) { return "Spoofed packet filter was matched on " + nic + " interface."; }
+            return "Filter " + handle + " was matched on " + nic + " interface.";
+        }
+
+        if (eventSensorType == 192) {
+            if (eventDataField[2] == 0) return "Security policy invoked. Some or all network traffic (TX) was stopped.";
+            if (eventDataField[2] == 2) return "Security policy invoked. Some or all network traffic (RX) was stopped.";
+            return "Security policy invoked.";
+        }
+
+        if (eventSensorType == 193) {
+            if ((eventDataField[0] == 0xAA) && (eventDataField[1] == 0x30) && (eventDataField[2] == 0x00) && (eventDataField[3] == 0x00)) { return "User request for remote connection."; }
+            if ((eventDataField[0] == 0xAA) && (eventDataField[1] == 0x20) && (eventDataField[2] == 0x03) && (eventDataField[3] == 0x01)) { return "EAC error: attempt to get posture while NAC in Intel(r) AMT is disabled."; } // eventDataField = 0xAA20030100000000
+            if ((eventDataField[0] == 0xAA) && (eventDataField[1] == 0x20) && (eventDataField[2] == 0x04) && (eventDataField[3] == 0x00)) { return "Certificate revoked. "; }
+        }
+
+        if (eventSensorType == 6) return "Authentication failed " + (eventDataField[1] + (eventDataField[2] << 8)) + " times. The system may be under attack.";
+        if (eventSensorType == 30) return "No bootable media";
+        if (eventSensorType == 32) return "Operating system lockup or power interrupt";
+        if (eventSensorType == 35) return "System boot failure";
+        if (eventSensorType == 37) return "System firmware started (at least one CPU is properly executing).";
+        return "Unknown Sensor Type #" + eventSensorType;
+    }
+
+// ###BEGIN###{AuditLog}
+
+    // Useful link: https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/default.htm?turl=WordDocuments%2Fsecurityadminevents.htm
+
+    var _AmtAuditStringTable =
+    {
+        16: 'Security Admin',
+        17: 'RCO',
+        18: 'Redirection Manager',
+        19: 'Firmware Update Manager',
+        20: 'Security Audit Log',
+        21: 'Network Time',
+        22: 'Network Administration',
+        23: 'Storage Administration',
+        24: 'Event Manager',
+        25: 'Circuit Breaker Manager',
+        26: 'Agent Presence Manager',
+        27: 'Wireless Configuration',
+        28: 'EAC',
+        29: 'KVM',
+        30: 'User Opt-In Events',
+        32: 'Screen Blanking',
+        33: 'Watchdog Events',
+        1600: 'Provisioning Started',
+        1601: 'Provisioning Completed',
+        1602: 'ACL Entry Added',
+        1603: 'ACL Entry Modified',
+        1604: 'ACL Entry Removed',
+        1605: 'ACL Access with Invalid Credentials',
+        1606: 'ACL Entry State',
+        1607: 'TLS State Changed',
+        1608: 'TLS Server Certificate Set',
+        1609: 'TLS Server Certificate Remove',
+        1610: 'TLS Trusted Root Certificate Added',
+        1611: 'TLS Trusted Root Certificate Removed',
+        1612: 'TLS Preshared Key Set',
+        1613: 'Kerberos Settings Modified',
+        1614: 'Kerberos Main Key Modified',
+        1615: 'Flash Wear out Counters Reset',
+        1616: 'Power Package Modified',
+        1617: 'Set Realm Authentication Mode',
+        1618: 'Upgrade Client to Admin Control Mode',
+        1619: 'Unprovisioning Started',
+        1700: 'Performed Power Up',
+        1701: 'Performed Power Down',
+        1702: 'Performed Power Cycle',
+        1703: 'Performed Reset',
+        1704: 'Set Boot Options',
+        1800: 'IDER Session Opened',
+        1801: 'IDER Session Closed',
+        1802: 'IDER Enabled',
+        1803: 'IDER Disabled',
+        1804: 'SoL Session Opened',
+        1805: 'SoL Session Closed',
+        1806: 'SoL Enabled',
+        1807: 'SoL Disabled',
+        1808: 'KVM Session Started',
+        1809: 'KVM Session Ended',
+        1810: 'KVM Enabled',
+        1811: 'KVM Disabled',
+        1812: 'VNC Password Failed 3 Times',
+        1900: 'Firmware Updated',
+        1901: 'Firmware Update Failed',
+        2000: 'Security Audit Log Cleared',
+        2001: 'Security Audit Policy Modified',
+        2002: 'Security Audit Log Disabled',
+        2003: 'Security Audit Log Enabled',
+        2004: 'Security Audit Log Exported',
+        2005: 'Security Audit Log Recovered',
+        2100: 'Intel(R) ME Time Set',
+        2200: 'TCPIP Parameters Set',
+        2201: 'Host Name Set',
+        2202: 'Domain Name Set',
+        2203: 'VLAN Parameters Set',
+        2204: 'Link Policy Set',
+        2205: 'IPv6 Parameters Set',
+        2300: 'Global Storage Attributes Set',
+        2301: 'Storage EACL Modified',
+        2302: 'Storage FPACL Modified',
+        2303: 'Storage Write Operation',
+        2400: 'Alert Subscribed',
+        2401: 'Alert Unsubscribed',
+        2402: 'Event Log Cleared',
+        2403: 'Event Log Frozen',
+        2500: 'CB Filter Added',
+        2501: 'CB Filter Removed',
+        2502: 'CB Policy Added',
+        2503: 'CB Policy Removed',
+        2504: 'CB Default Policy Set',
+        2505: 'CB Heuristics Option Set',
+        2506: 'CB Heuristics State Cleared',
+        2600: 'Agent Watchdog Added',
+        2601: 'Agent Watchdog Removed',
+        2602: 'Agent Watchdog Action Set',
+        2700: "Wireless Profile Added",
+        2701: "Wireless Profile Removed",
+        2702: "Wireless Profile Updated",
+        2703: "An existing profile sync was modified",
+        2704: "An existing profile link preference was changed",
+        2705: "Wireless profile share with UEFI enabled setting was changed",
+        2800: 'EAC Posture Signer SET',
+        2801: 'EAC Enabled',
+        2802: 'EAC Disabled',
+        2803: 'EAC Posture State',
+        2804: 'EAC Set Options',
+        2900: 'KVM Opt-in Enabled',
+        2901: 'KVM Opt-in Disabled',
+        2902: 'KVM Password Changed',
+        2903: 'KVM Consent Succeeded',
+        2904: 'KVM Consent Failed',
+        3000: 'Opt-In Policy Change',
+        3001: 'Send Consent Code Event',
+        3002: 'Start Opt-In Blocked Event'
+    }
+
+    // Return human readable extended audit log data
+    // TODO: Just put some of them here, but many more still need to be added, helpful link here:
+    // https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/default.htm?turl=WordDocuments%2Fsecurityadminevents.htm
+    obj.GetAuditLogExtendedDataStr = function (id, data) {
+        if ((id == 1602 || id == 1604) && data[0] == 0) { return bufToArray(data).splice(2, 2 + data[1]).toString(); } // ACL Entry Added/Removed (Digest)
+        if (id == 1603) { if (data[1] == 0) { return bufToArray(data).splice(3).toString(); } return null; } // ACL Entry Modified
+        if (id == 1605) { return ["Invalid ME access", "Invalid MEBx access"][data[0]]; } // ACL Access with Invalid Credentials
+        if (id == 1606) { var r = ["Disabled", "Enabled"][data[0]]; if (data[1] == 0) { r += ", " + data[3]; } return r; } // ACL Entry State
+        if (id == 1607) { return "Remote " + ["NoAuth", "ServerAuth", "MutualAuth"][data[0]] + ", Local " + ["NoAuth", "ServerAuth", "MutualAuth"][data[1]]; } // TLS State Changed
+        if (id == 1617) { return obj.RealmNames[ReadInt(data, 0)] + ", " + ["NoAuth", "Auth", "Disabled"][data[4]]; } // Set Realm Authentication Mode
+        if (id == 1619) { return ["BIOS", "MEBx", "Local MEI", "Local WSMAN", "Remote WSAMN"][data[0]]; } // Intel AMT Unprovisioning Started
+        if (id == 1900) { return "From " + ReadShort(data, 0) + "." + ReadShort(data, 2) + "." + ReadShort(data, 4) + "." + ReadShort(data, 6) + " to " + ReadShort(data, 8) + "." + ReadShort(data, 10) + "." + ReadShort(data, 12) + "." + ReadShort(data, 14); } // Firmware Updated
+        if (id == 2100) { var t4 = new Date(); t4.setTime(ReadInt(data, 0) * 1000 + (new Date().getTimezoneOffset() * 60000)); return t4.toLocaleString(); } // Intel AMT Time Set
+        if (id == 3000) { return "From " + ["None", "KVM", "All"][data[0]] + " to " + ["None", "KVM", "All"][data[1]]; } // Opt-In Policy Change
+        if (id == 3001) { return ["Success", "Failed 3 times"][data[0]]; } // Send Consent Code Event
+        return null;
+    }
+
+    obj.GetAuditLog = function (func) {
+        obj.AMT_AuditLog_ReadRecords(1, _GetAuditLog0, [func, []]);
+    }
+
+    function MakeToArray(v) { if (!v || v == null || typeof v == 'object') return v; return [v]; }
+    function ReadShort(v, p) { return (v[p] << 8) + v[p + 1]; }
+    function ReadInt(v, p) { return (v[p] * 0x1000000) + (v[p + 1] << 16) + (v[p + 2] << 8) + v[p + 3]; } // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
+    function ReadIntX(v, p) { return (v[p + 3] * 0x1000000) + (v[p + 2] << 16) + (v[p + 1] << 8) + v[p]; }
+    function btoa(x) { return Buffer.from(x).toString('base64'); }
+    function atob(x) { var z = null; try { z = Buffer.from(x, 'base64').toString(); } catch (e) { console.log(e); } return z; }
+    function bufToArray(buf) { var r = []; for (var i in buf) { r.push(buf[i]); } return r; }
+
+    function _GetAuditLog0(stack, name, responses, status, tag) {
+        if (status != 200) { tag[0](obj, [], status); return; }
+        var ptr, i, e, x, r = tag[1], t = new Date(), TimeStamp;
+
+        if (responses.Body['RecordsReturned'] > 0) {
+            responses.Body['EventRecords'] = MakeToArray(responses.Body['EventRecords']);
+
+            for (i in responses.Body['EventRecords']) {
+                e = null;
+                try {
+                    e = Buffer.from(responses.Body['EventRecords'][i], 'base64');
+                } catch (ex) {
+                    console.log(ex + " " + responses.Body['EventRecords'][i])
+                }
+
+                x = { 'AuditAppID': ReadShort(e, 0), 'EventID': ReadShort(e, 2), 'InitiatorType': e[4] };
+                x['AuditApp'] = _AmtAuditStringTable[x['AuditAppID']];
+                x['Event'] = _AmtAuditStringTable[(x['AuditAppID'] * 100) + x['EventID']];
+                if (!x['Event']) x['Event'] = '#' + x['EventID'];
+
+                // Read and process the initiator
+                if (x['InitiatorType'] == 0) {
+                    // HTTP digest
+                    var userlen = e[5];
+                    x['Initiator'] = e.slice(6, 6 + userlen).toString();
+                    ptr = 6 + userlen;
+                }
+                if (x['InitiatorType'] == 1) {
+                    // Kerberos
+                    x['KerberosUserInDomain'] = ReadInt(e, 5);
+                    var userlen = e[9];
+                    x['Initiator'] = GetSidString(e.slice(10, 10 + userlen));
+                    ptr = 10 + userlen;
+                }
+                if (x['InitiatorType'] == 2) {
+                    // Local
+                    x['Initiator'] = 'Local';
+                    ptr = 5;
+                }
+                if (x['InitiatorType'] == 3) {
+                    // KVM Default Port
+                    x['Initiator'] = 'KVM Default Port';
+                    ptr = 5;
+                }
+
+                // Read timestamp
+                TimeStamp = ReadInt(e, ptr);
+                x['Time'] = new Date((TimeStamp + (t.getTimezoneOffset() * 60)) * 1000);
+                ptr += 4;
+
+                // Read network access
+                x['MCLocationType'] = e[ptr++];
+                var netlen = e[ptr++];
+                x['NetAddress'] = e.slice(ptr, ptr + netlen).toString();
+
+                // Read extended data
+                ptr += netlen;
+                var exlen = e[ptr++];
+                x['Ex'] = e.slice(ptr, ptr + exlen);
+                x['ExStr'] = obj.GetAuditLogExtendedDataStr((x['AuditAppID'] * 100) + x['EventID'], x['Ex']);
+                r.push(x);
+            }
+        }
+        if (responses.Body['TotalRecordCount'] > r.length) {
+            obj.AMT_AuditLog_ReadRecords(r.length + 1, _GetAuditLog0, [tag[0], r]);
+        } else {
+            tag[0](obj, r, status);
+        }
+    }
+
+    // ###END###{AuditLog}
+
+    /*
+    // ###BEGIN###{Certificates}
+
+    // Forge MD5
+    function hex_md5(str) { return forge.md.md5.create().update(str).digest().toHex(); }
+
+    // ###END###{Certificates}
+
+    // ###BEGIN###{!Certificates}
+
+    // TinyMD5 from https://github.com/jbt/js-crypto
+
+    // Perform MD5 setup
+    var md5_k = [];
+    for (var i = 0; i < 64;) { md5_k[i] = 0 | (Math.abs(Math.sin(++i)) * 4294967296); }
+
+    // Perform MD5 on raw string and return hex
+    function hex_md5(str) {
+        var b, c, d, j,
+        x = [],
+        str2 = unescape(encodeURI(str)),
+        a = str2.length,
+        h = [b = 1732584193, c = -271733879, ~b, ~c],
+        i = 0;
+
+        for (; i <= a;) x[i >> 2] |= (str2.charCodeAt(i) || 128) << 8 * (i++ % 4);
+
+        x[str = (a + 8 >> 6) * 16 + 14] = a * 8;
+        i = 0;
+
+        for (; i < str; i += 16) {
+            a = h; j = 0;
+            for (; j < 64;) {
+                a = [
+                  d = a[3],
+                  ((b = a[1] | 0) +
+                    ((d = (
+                      (a[0] +
+                        [
+                          b & (c = a[2]) | ~b & d,
+                          d & b | ~d & c,
+                          b ^ c ^ d,
+                          c ^ (b | ~d)
+                        ][a = j >> 4]
+                      ) +
+                      (md5_k[j] +
+                        (x[[
+                          j,
+                          5 * j + 1,
+                          3 * j + 5,
+                          7 * j
+                        ][a] % 16 + i] | 0)
+                      )
+                    )) << (a = [
+                      7, 12, 17, 22,
+                      5, 9, 14, 20,
+                      4, 11, 16, 23,
+                      6, 10, 15, 21
+                    ][4 * a + j++ % 4]) | d >>> 32 - a)
+                  ),
+                  b,
+                  c
+                ];
+            }
+            for (j = 4; j;) h[--j] = h[j] + a[j];
+        }
+
+        str = '';
+        for (; j < 32;) str += ((h[j >> 3] >> ((1 ^ j++ & 7) * 4)) & 15).toString(16);
+        return str;
+    }
+
+    // ###END###{!Certificates}
+
+    // Perform MD5 on raw string and return raw string result
+    function rstr_md5(str) { return hex2rstr(hex_md5(str)); }
+    */
+    /*
+    Convert arguments into selector set and body XML. Used by AMT_WiFiPortConfigurationService_UpdateWiFiSettings.
+    args = { 
+        "WiFiEndpoint": {
+            __parameterType: 'reference',
+            __resourceUri: 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpoint',
+            Name: 'WiFi Endpoint 0'
+        }, 
+        "WiFiEndpointSettingsInput": 
+        {
+            __parameterType: 'instance',
+            __namespace: 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpointSettings',
+            ElementName: document.querySelector('#editProfile-profileName').value,
+            InstanceID: 'Intel(r) AMT:WiFi Endpoint Settings ' + document.querySelector('#editProfile-profileName').value,
+            AuthenticationMethod: document.querySelector('#editProfile-networkAuthentication').value,
+            //BSSType: 3, // Intel(r) AMT supports only infrastructure networks
+            EncryptionMethod: document.querySelector('#editProfile-encryption').value,
+            SSID: document.querySelector('#editProfile-networkName').value,
+            Priority: 100,
+            PSKPassPhrase: document.querySelector('#editProfile-passPhrase').value
+        }, 
+        "IEEE8021xSettingsInput": null, 
+        "ClientCredential": null, 
+        "CACredential": null 
+    }, 
+    */
+    function execArgumentsToXml(args) {
+        if (args === undefined || args === null) return null;
+
+        var result = '';
+        for (var argName in args) {
+            var arg = args[argName];
+            if (!arg) continue;
+            if (arg['__parameterType'] === 'reference') result += referenceToXml(argName, arg);
+            else result += instanceToXml(argName, arg);
+            //if(arg['__isInstance']) result += instanceToXml(argName, arg);
+        }
+        return result;
+    }
+
+    /**
+     * Convert JavaScript object into XML
+     
+        <r:WiFiEndpointSettingsInput xmlns:q="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpointSettings">
+            <q:ElementName>Wireless-Profile-Admin</q:ElementName>
+            <q:InstanceID>Intel(r) AMT:WiFi Endpoint Settings Wireless-Profile-Admin</q:InstanceID>
+            <q:AuthenticationMethod>6</q:AuthenticationMethod>
+            <q:EncryptionMethod>4</q:EncryptionMethod>
+            <q:Priority>100</q:Priority>
+            <q:PSKPassPhrase>P@ssw0rd</q:PSKPassPhrase>
+        </r:WiFiEndpointSettingsInput>
+     */
+    function instanceToXml(instanceName, inInstance) {
+        if (inInstance === undefined || inInstance === null) return null;
+
+        var hasNamespace = !!inInstance['__namespace'];
+        var startTag = hasNamespace ? '<q:' : '<';
+        var endTag = hasNamespace ? '</q:' : '</';
+        var namespaceDef = hasNamespace ? (' xmlns:q="' + inInstance['__namespace'] + '"') : '';
+        var result = '<r:' + instanceName + namespaceDef + '>';
+        for (var prop in inInstance) {
+            if (!inInstance.hasOwnProperty(prop) || prop.indexOf('__') === 0) continue;
+
+            if (typeof inInstance[prop] === 'function' || Array.isArray(inInstance[prop])) continue;
+
+            if (typeof inInstance[prop] === 'object') {
+                //result += startTag + prop +'>' + instanceToXml('prop', inInstance[prop]) + endTag + prop +'>';
+                console.error('only convert one level down...');
+            }
+            else {
+                result += startTag + prop + '>' + inInstance[prop].toString() + endTag + prop + '>';
+            }
+        }
+        result += '</r:' + instanceName + '>';
+        return result;
+    }
+
+
+    /**
+     * Convert a selector set into XML. Expect no nesting.
+     * {
+     * 	selectorName : selectorValue,
+     * 	selectorName : selectorValue,
+     *	... ...
+     * }
+     
+        <r:WiFiEndpoint>
+            <a:Address>http://192.168.1.103:16992/wsman</a:Address>
+            <a:ReferenceParameters>
+                <w:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpoint</w:ResourceURI>
+                <w:SelectorSet>
+                    <w:Selector Name="Name">WiFi Endpoint 0</w:Selector>
+                </w:SelectorSet>
+            </a:ReferenceParameters>
+        </r:WiFiEndpoint>
+                
+     */
+    function referenceToXml(referenceName, inReference) {
+        if (inReference === undefined || inReference === null) return null;
+
+        var result = '<r:' + referenceName + '><a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>' + inReference['__resourceUri'] + '</w:ResourceURI><w:SelectorSet>';
+        for (var selectorName in inReference) {
+            if (!inReference.hasOwnProperty(selectorName) || selectorName.indexOf('__') === 0) continue;
+
+            if (typeof inReference[selectorName] === 'function' ||
+                typeof inReference[selectorName] === 'object' ||
+                Array.isArray(inReference[selectorName]))
+                continue;
+
+            result += '<w:Selector Name="' + selectorName + '">' + inReference[selectorName].toString() + '</w:Selector>';
+        }
+
+        result += '</w:SelectorSet></a:ReferenceParameters></r:' + referenceName + '>';
+        return result;
+    }
+
+    // Convert a byte array of SID into string
+    function GetSidString(sid) {
+        var r = 'S-' + sid[0] + '-' + sid[7];
+        for (var i = 2; i < (sid.length / 4); i++) r += '-' + ReadIntX(sid, i * 4);
+        return r;
+    }
+
+    // Convert a SID readable string into bytes
+    function GetSidByteArray(sidString) {
+        if (!sidString || sidString == null) return null;
+        var sidParts = sidString.split('-');
+
+        // Make sure the SID has at least 4 parts and starts with 'S'
+        if (sidParts.length < 4 || (sidParts[0] != 's' && sidParts[0] != 'S')) return null;
+
+        // Check that each part of the SID is really an integer
+        for (var i = 1; i < sidParts.length; i++) { var y = parseInt(sidParts[i]); if (y != sidParts[i]) return null; sidParts[i] = y; }
+
+        // Version (8 bit) + Id count (8 bit) + 48 bit in big endian -- DO NOT use bitwise right shift operator. JavaScript converts the number into a 32 bit integer before shifting. In real world, it's highly likely this part is always 0.
+        var r = String.fromCharCode(sidParts[1]) + String.fromCharCode(sidParts.length - 3) + ShortToStr(Math.floor(sidParts[2] / Math.pow(2, 32))) + IntToStr((sidParts[2]) & 0xFFFF);
+
+        // the rest are in 32 bit in little endian
+        for (var i = 3; i < sidParts.length; i++) r += IntToStrX(sidParts[i]);
+        return r;
+    }
+
+    return obj;
+}
+
+module.exports = AmtStackCreateService;

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 37 - 0
agents/modules_meshcmd/linux-dhcp.js


+ 359 - 0
agents/modules_meshcmd/smbios.js

@@ -0,0 +1,359 @@
+/*
+Copyright 2018 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+try { Object.defineProperty(Array.prototype, "peek", { value: function () { return (this.length > 0 ? this[this.length - 1] : undefined); } }); } catch (e) { }
+try { Object.defineProperty(String.prototype, "replaceAll", { value: function replaceAll(oldVal, newVal) { return (this.split(oldVal).join(newVal)); } }); } catch (e) { }
+
+var RSMB = 1381190978;
+var memoryLocation = { 0x1: 'Other', 0x2: 'Unknown', 0x3: 'System Board', 0x4: 'ISA', 0x5: 'EISA', 0x6: 'PCI', 0x7: 'MCA', 0x8: 'PCMCIA', 0x9: 'Proprietary', 0xA: 'NuBus', 0xA0: 'PC-98/C20', 0xA1: 'PC-98/C24', 0xA2: 'PC-98/E', 0xA3: 'PC-98/LB' };
+var wakeReason = ['Reserved', 'Other', 'Unknown', 'APM Timer', 'Modem Ring', 'LAN', 'Power Switch', 'PCI', 'AC Power'];
+
+// Fill the left with zeros until the string is of a given length
+function zeroLeftPad(str, len)
+{
+    if ((len == null) && (typeof (len) != 'number')) { return null; }
+    if (str == null) str = ''; // If null, this is to generate zero leftpad string
+    var zlp = '';
+    for (var i = 0; i < len - str.length; i++) { zlp += '0'; }
+    return zlp + str;
+}
+
+function SMBiosTables()
+{
+    this._ObjectID = 'SMBiosTable';
+    if (process.platform == 'win32') {
+        this._marshal = require('_GenericMarshal');
+        this._native = this._marshal.CreateNativeProxy("Kernel32.dll");
+
+        this._native.CreateMethod('EnumSystemFirmwareTables');
+        this._native.CreateMethod('GetSystemFirmwareTable');
+    }
+    if (process.platform == 'linux') {
+        this._canonicalizeData = function _canonicalizeData(data) {
+            var lines = data.toString().split('Header and Data:\x0A');
+            var MemoryStream = require('MemoryStream');
+            var ms = new MemoryStream();
+
+            for (var i = 1; i < lines.length; ++i) {
+                var tokens = lines[i].split('Strings:\x0A');
+                var header = tokens[0].split('\x0A\x0A')[0].replaceAll('\x0A', '').trim().replaceAll(' ', '').replaceAll('\x09', '');
+                ms.write(Buffer.from(header, 'hex'));
+                if (tokens.length > 1) {
+                    var strings = tokens[1].split('\x0A\x0A')[0].split('\x0A');
+                    var stringsFinal = [];
+                    for (var strx in strings) {
+                        var tmp = strings[strx].trim().replaceAll(' ', '').replaceAll('\x09', '');
+                        if (tmp && tmp[0] !== '"' && /^[0-9a-fA-F]+$/.test(tmp)) { stringsFinal.push(tmp); }
+                    }
+                    ms.write(Buffer.from(stringsFinal.join(''), 'hex'));
+                    ms.write(Buffer.from('00', 'hex'));
+                }
+                else {
+                    ms.write(Buffer.from('0000', 'hex'));
+                }
+            }
+            var retVal = ms.buffer;
+            retVal.ms = ms;
+            return (retVal);
+        };
+    }
+    this._parse = function _parse(SMData) {
+        var ret = {};
+        var pbyte;
+        var i = 0
+        var SMData;
+        var structcount = 0;
+
+        while (SMData && i < SMData.length)
+        {
+            var SMtype = SMData[i];
+            var SMlength = SMData[i + 1];
+
+            if (!ret[SMtype]) { ret[SMtype] = []; }
+            ret[SMtype].push(SMData.slice(i + 4, i + SMlength));
+            if (process.platform == 'win32') { ret[SMtype].peek()._ext = pbyte; }
+            i += SMlength;
+
+            ret[SMtype].peek()._strings = [];
+
+            while (SMData[i] != 0 && i <= SMData.length)
+            {
+                var strstart = i;
+
+                // Start of String, find end of string
+                while (SMData[i++] != 0 && i <= SMData.length);
+                try
+                {
+                    ret[SMtype].peek()._strings.push(SMData.slice(strstart, i).toString().trim());
+                }
+                catch (ee)
+                {
+                }
+            }
+            i += (ret[SMtype].peek()._strings.length == 0) ? 2 : 1;
+            ++structcount;
+            //console.log('End of Table[' + SMtype + ']: ' + i);
+        }
+        //console.log('Struct Count = ' + structcount);
+        return (ret);
+    };
+    this.get = function get(callback) {
+        if (process.platform == 'win32') {
+            var size = this._native.GetSystemFirmwareTable(RSMB, 0, 0, 0).Val;
+            //console.log('Table Size: ' + size);
+
+            var PtrSize = this._marshal.CreatePointer()._size;
+            var buffer = this._marshal.CreateVariable(size);
+            var written = this._native.GetSystemFirmwareTable(RSMB, 0, buffer, size).Val;
+            //console.log('Written Size: ' + written);
+
+            var rawBuffer = buffer.toBuffer();
+            var length = buffer.Deref(4, 4).toBuffer().readUInt32LE(0);
+
+            pbyte = buffer.Deref(8, length);
+            SMData = pbyte.toBuffer();
+
+            if (callback) { callback.apply(this, [this._parse(SMData)]); return; } else { return (this._parse(SMData)); }
+        }
+        if (process.platform == 'linux') {
+            var MemoryStream = require('MemoryStream');
+            this.child = require('child_process').execFile('/usr/sbin/dmidecode', ['dmidecode', '-u']);
+            this.child.SMBiosTable = this;
+            this.child.ms = new MemoryStream();
+            this.child.ms.callback = callback;
+            this.child.ms.child = this.child;
+            this.child.stdout.on('data', function (buffer) { this.parent.ms.write(buffer); });
+            this.child.on('exit', function () { this.ms.end(); });
+            this.child.ms.on('end', function () {
+                //console.log('read ' + this.buffer.length + ' bytes');
+                if (this.buffer.length < 300) {
+                    //console.log('Not enough permission to read SMBiosTable');
+                    if (this.callback) { this.callback.apply(this.child.SMBiosTable, []); }
+                }
+                else {
+                    var SMData = this.child.SMBiosTable._canonicalizeData(this.buffer);
+                    var j = this.child.SMBiosTable._parse(SMData);
+                    if (this.callback) { this.callback.apply(this.child.SMBiosTable, [j]); }
+                }
+            });
+            return;
+        }
+        if (callback) { callback.apply(this, [null]); return; } else { return (null); }
+    };
+    this.parse = function parse(data) {
+        var r = {};
+        try
+        {
+            r.processorInfo = this.processorInfo(data);
+        }
+        catch(e)
+        {
+        }
+        try
+        {
+            r.memoryInfo = this.memoryInfo(data);
+        }
+        catch(e)
+        {
+        }
+        try
+        {
+            r.systemInfo = this.systemInfo(data);
+        }
+        catch(e)
+        {
+        }
+        try
+        {
+            r.systemSlots = this.systemSlots(data);
+        }
+        catch(e)
+        {
+        }
+        try
+        {
+            r.amtInfo = this.amtInfo(data);
+        }
+        catch(e)
+        {
+        }
+        try
+        {
+            if (JSON.stringify(r).length > 65535) { r = {}; }
+        }
+        catch(ee)
+        {}
+        return r;
+    }
+    this.processorInfo = function processorInfo(data) {
+        if (!data) { throw ('no data'); }
+        var ret = [];
+        var ptype = ['ERROR', 'Other', 'Unknown', 'CPU', 'ALU', 'DSP', 'GPU'];
+        var statusString = ['Unknown', 'Enabled', 'Disabled by user', 'Disabled by BIOS', 'Idle', 'Reserved', 'Reserved', 'Other'];
+        var cpuid = 0;
+        while (data[4] && data[4].length > 0) {
+            var p = data[4].pop();
+            var populated = p[20] & 0x40;
+            var status = p[20] & 0x07
+            if (populated) {
+                var j = { _ObjectID: 'SMBiosTables.processorInfo' };
+                j.Processor = ptype[p[1]];
+                j.MaxSpeed = p.readUInt16LE(16) + ' Mhz';
+                if (p[31]) { j.Cores = p[31]; }
+                if (p[33]) { j.Threads = p[33]; }
+                j.Populated = 1;
+                j.Status = statusString[status];
+                j.Socket = p._strings[p[0] - 1];
+                j.Manufacturer = p._strings[p[3] - 1];
+                j.Version = p._strings[p[12] - 1];
+                ret.push(j);
+            }
+        }
+        return (ret);
+    };
+    this.memoryInfo = function memoryInfo(data) {
+        if (!data) { throw ('no data'); }
+        var retVal = { _ObjectID: 'SMBiosTables.memoryInfo' };
+        if (data[16]) {
+            var m = data[16].peek();
+            retVal.location = memoryLocation[m[0]];
+            if ((retVal.maxCapacityKb = m.readUInt32LE(3)) == 0x80000000) {
+                retVal.maxCapacityKb = 'A really big number';
+            }
+        }
+        return (retVal);
+    };
+    this.systemInfo = function systemInfo(data)
+    {
+        if (!data) { throw ('no data'); }
+        var retVal = { _ObjectID: 'SMBiosTables.systemInfo' };
+        if (data[1])
+        {
+            var si = data[1].peek();
+            var uuid = si.slice(4, 20);
+
+            retVal.uuid = [zeroLeftPad(uuid.readUInt32LE(0).toString(16), 8),
+            zeroLeftPad(uuid.readUInt16LE(4).toString(16), 4),
+            zeroLeftPad(uuid.readUInt16LE(6).toString(16), 4),
+            zeroLeftPad(uuid.readUInt16BE(8).toString(16), 4),
+            zeroLeftPad(uuid.slice(10).toString('hex').toLowerCase(), 12)].join('-');
+
+            retVal.wakeReason = wakeReason[si[20]];
+        }
+        return (retVal);
+    };
+    this.systemSlots = function systemSlots(data) {
+        if (!data) { throw ('no data'); }
+        var retVal = [];
+        if (data[9]) {
+            while (data[9].length > 0) {
+                var ss = data[9].pop();
+                retVal.push({ name: ss._strings[ss[0] - 1] });
+            }
+        }
+        return (retVal);
+    };
+    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')
+        {
+            var amt = data[130].peek();
+            retVal.AMT = amt[4] ? true : false;
+            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')
+                {
+                    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; }
+                    retVal.MEBX = settings.readUInt16LE(4).toString() + '.' + settings.readUInt16LE(6).toString() + '.' + settings.readUInt16LE(8).toString() + '.' + settings.readUInt16LE(10).toString();
+
+                    var mecap = settings.slice(20, 32);
+                    retVal.ManagementEngine = mecap.readUInt16LE(6).toString() + '.' + mecap.readUInt16LE(4).toString() + '.' + mecap.readUInt16LE(10).toString() + '.' + mecap.readUInt16LE(8).toString();
+
+                    //var lan = settings.slice(36, 48);
+                    //console.log(lan.toString('hex'));
+                    //retVal.LAN = (lan.readUInt16LE(10) & 0x03).toString() + '/' + ((lan.readUInt16LE(10) & 0xF8) >> 3).toString();
+
+                    //console.log(lan.readUInt16LE(3));
+                    //retVal.WLAN = (lan.readUInt16LE(3) & 0x07).toString() + '/' + ((lan.readUInt16LE(3) & 0xF8) >> 3).toString() + '/' + (lan.readUInt16LE(3) >> 8).toString();
+                }
+            }
+        }
+        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 = {
+        0: 'BIOS information',
+        1: 'System information',
+        2: 'Baseboard (or Module) information',
+        4: 'Processor information',
+        5: 'memory controller information',
+        6: 'Memory module information',
+        7: 'Cache information',
+        8: 'Port connector information',
+        9: 'System slots',
+        10: 'On board devices information',
+        11: 'OEM strings',
+        12: 'System configuration options',
+        13: 'BIOS language information',
+        14: 'Group associations',
+        15: 'System event log',
+        16: 'Physical memory array',
+        17: 'Memory device',
+        18: '32bit memory error information',
+        19: 'Memory array mapped address',
+        20: 'Memory device mapped address',
+        21: 'Built-in pointing device',
+        22: 'Portable battery',
+        23: 'System reset',
+        24: 'Hardware security',
+        25: 'System power controls',
+        26: 'Voltage probe',
+        27: 'Cooling device',
+        28: 'Temperature probe',
+        29: 'Electrical current probe',
+        30: 'Out-of-band remote access',
+        31: 'Boot integrity services (BIS) entry point',
+        32: 'System boot information',
+        33: '64bit memory error information',
+        34: 'Management device',
+        35: 'Management device component',
+        36: 'Management device threshold data',
+        37: 'Memory channel',
+        38: 'IPMI device information',
+        39: 'System power supply',
+        40: 'Additional information',
+        41: 'Onboard devices extended information',
+        42: 'Management controller host interface',
+        126: 'Inactive',
+        127: 'End-of-table'
+    }
+}
+
+module.exports = new SMBiosTables();

+ 289 - 0
agents/modules_meshcmd/sysinfo.js

@@ -0,0 +1,289 @@
+/*
+Copyright 2019-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+var PDH_FMT_LONG = 0x00000100;
+var PDH_FMT_DOUBLE = 0x00000200;
+
+var promise = require('promise');
+if (process.platform == 'win32')
+{
+    var GM = require('_GenericMarshal');
+    GM.kernel32 = GM.CreateNativeProxy('kernel32.dll');
+    GM.kernel32.CreateMethod('GlobalMemoryStatusEx');
+
+    GM.pdh = GM.CreateNativeProxy('pdh.dll');
+    GM.pdh.CreateMethod('PdhAddEnglishCounterA');
+    GM.pdh.CreateMethod('PdhCloseQuery');
+    GM.pdh.CreateMethod('PdhCollectQueryData');
+    GM.pdh.CreateMethod('PdhGetFormattedCounterValue');
+    GM.pdh.CreateMethod('PdhGetFormattedCounterArrayA');
+    GM.pdh.CreateMethod('PdhOpenQueryA');
+    GM.pdh.CreateMethod('PdhRemoveCounter');
+}
+
+function windows_cpuUtilization()
+{
+    var p = new promise(function (res, rej) { this._res = res; this._rej = rej; });
+    p.counter = GM.CreateVariable(16);
+    p.cpu = GM.CreatePointer();
+    p.cpuTotal = GM.CreatePointer();
+    var err = 0;
+    if ((err = GM.pdh.PdhOpenQueryA(0, 0, p.cpu).Val) != 0) { p._rej(err); return; }
+
+    // This gets the CPU Utilization for each proc
+    if ((err = GM.pdh.PdhAddEnglishCounterA(p.cpu.Deref(), GM.CreateVariable('\\Processor(*)\\% Processor Time'), 0, p.cpuTotal).Val) != 0) { p._rej(err); return; }
+
+    if ((err = GM.pdh.PdhCollectQueryData(p.cpu.Deref()).Val != 0)) { p._rej(err); return; }
+    p._timeout = setTimeout(function (po)
+    {
+        var u = { cpus: [] };
+        var bufSize = GM.CreateVariable(4);
+        var itemCount = GM.CreateVariable(4);
+        var buffer, szName, item;
+        var e;
+        if ((e = GM.pdh.PdhCollectQueryData(po.cpu.Deref()).Val != 0)) { po._rej(e); return; }
+
+        if ((e = GM.pdh.PdhGetFormattedCounterArrayA(po.cpuTotal.Deref(), PDH_FMT_DOUBLE, bufSize, itemCount, 0).Val) == -2147481646)
+        {
+            buffer = GM.CreateVariable(bufSize.toBuffer().readUInt32LE());
+        }
+        else
+        {
+            po._rej(e);
+            return;
+        }
+        if ((e = GM.pdh.PdhGetFormattedCounterArrayA(po.cpuTotal.Deref(), PDH_FMT_DOUBLE, bufSize, itemCount, buffer).Val) != 0) { po._rej(e); return; }
+        for(var i=0;i<itemCount.toBuffer().readUInt32LE();++i)
+        {
+            item = buffer.Deref(i * 24, 24);
+            szName = item.Deref(0, GM.PointerSize).Deref();
+            if (szName.String == '_Total')
+            {
+                u.total = item.Deref(16, 8).toBuffer().readDoubleLE();
+            }
+            else
+            {
+                u.cpus[parseInt(szName.String)] = item.Deref(16, 8).toBuffer().readDoubleLE();
+            }
+        }
+
+        GM.pdh.PdhRemoveCounter(po.cpuTotal.Deref());
+        GM.pdh.PdhCloseQuery(po.cpu.Deref());
+        p._res(u);
+    }, 100, p);
+
+    return (p);
+}
+function windows_memUtilization()
+{
+    var info = GM.CreateVariable(64);
+    info.Deref(0, 4).toBuffer().writeUInt32LE(64);
+    GM.kernel32.GlobalMemoryStatusEx(info);
+
+    var ret =
+        {
+            MemTotal: require('bignum').fromBuffer(info.Deref(8, 8).toBuffer(), { endian: 'little' }),
+            MemFree: require('bignum').fromBuffer(info.Deref(16, 8).toBuffer(), { endian: 'little' })
+        };
+
+    ret.percentFree = ((ret.MemFree.div(require('bignum')('1048576')).toNumber() / ret.MemTotal.div(require('bignum')('1048576')).toNumber()) * 100);//.toFixed(2);
+    ret.percentConsumed = ((ret.MemTotal.sub(ret.MemFree).div(require('bignum')('1048576')).toNumber() / ret.MemTotal.div(require('bignum')('1048576')).toNumber()) * 100);//.toFixed(2);
+    ret.MemTotal = ret.MemTotal.toString();
+    ret.MemFree = ret.MemFree.toString();
+    return (ret);
+}
+
+var cpuLastIdle = [];
+var cpuLastSum = [];
+function linux_cpuUtilization() {
+    var ret = { cpus: [] };
+    var info = require('fs').readFileSync('/proc/stat');
+    var lines = info.toString().split('\n');
+    var columns;
+    var x, y;
+    var cpuNo = 0;
+    var currSum, currIdle, utilization;
+    for (var i in lines) {
+        columns = lines[i].split(' ');
+        if (!columns[0].startsWith('cpu')) { break; }
+
+        x = 0, currSum = 0;
+        while (columns[++x] == '');
+        for (y = x; y < columns.length; ++y) { currSum += parseInt(columns[y]); }
+        currIdle = parseInt(columns[3 + x]);
+
+        var diffIdle = currIdle - cpuLastIdle[cpuNo];
+        var diffSum = currSum - cpuLastSum[cpuNo];
+
+        utilization = (100 - ((diffIdle / diffSum) * 100));
+
+        cpuLastSum[cpuNo] = currSum;
+        cpuLastIdle[cpuNo] = currIdle;
+
+        if (!ret.total) {
+            ret.total = utilization;
+        } else {
+            ret.cpus.push(utilization);
+        }
+        ++cpuNo;
+    }
+
+    var p = new promise(function (res, rej) { this._res = res; this._rej = rej; });
+    p._res(ret);
+    return (p);
+}
+function linux_memUtilization()
+{
+    var ret = {};
+
+    var info = require('fs').readFileSync('/proc/meminfo').toString().split('\n');
+    var tokens;
+    for(var i in info)
+    {
+        tokens = info[i].split(' ');
+        switch(tokens[0])
+        {
+            case 'MemTotal:':
+                ret.total = parseInt(tokens[tokens.length - 2]);
+                break;
+            case 'MemFree:':
+                ret.free = parseInt(tokens[tokens.length - 2]);
+                break;
+        }
+    }
+    ret.percentFree = ((ret.free / ret.total) * 100);//.toFixed(2);
+    ret.percentConsumed = (((ret.total - ret.free) / ret.total) * 100);//.toFixed(2);
+    return (ret);
+}
+
+function macos_cpuUtilization()
+{
+    var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
+    var child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = '';
+    child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
+    child.stdin.write('top -l 1 | grep -E "^CPU"\nexit\n');
+    child.waitExit();
+
+    var lines = child.stdout.str.split('\n');
+    if (lines[0].length > 0)
+    {
+        var usage = lines[0].split(':')[1];
+        var bdown = usage.split(',');
+
+        var tot = parseFloat(bdown[0].split('%')[0].trim()) + parseFloat(bdown[1].split('%')[0].trim());
+        ret._res({total: tot, cpus: []});
+    }
+    else
+    {
+        ret._rej('parse error');
+    }
+
+    return (ret);
+}
+function macos_memUtilization()
+{
+    var mem = { };
+    var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
+    var child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = '';
+    child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
+    child.stdin.write('top -l 1 | grep -E "^Phys"\nexit\n');
+    child.waitExit();
+
+    var lines = child.stdout.str.split('\n');
+    if (lines[0].length > 0)
+    {
+        var usage = lines[0].split(':')[1];
+        var bdown = usage.split(',');
+
+        mem.MemTotal = parseInt(bdown[0].trim().split(' ')[0]);
+        mem.MemFree = parseInt(bdown[1].trim().split(' ')[0]);
+        mem.percentFree = ((mem.MemFree / mem.MemTotal) * 100);//.toFixed(2);
+        mem.percentConsumed = (((mem.MemTotal - mem.MemFree) / mem.MemTotal) * 100);//.toFixed(2);
+        return (mem);
+    }
+    else
+    {
+        throw ('Parse Error');
+    }
+}
+
+function windows_thermals()
+{
+    var ret = [];
+    try {
+        ret = require('win-wmi').query('ROOT\\WMI', 'SELECT CurrentTemperature,InstanceName FROM MSAcpi_ThermalZoneTemperature',['CurrentTemperature','InstanceName']);
+        if (ret[0]) {
+            for (var i = 0; i < ret.length; ++i) {
+                ret[i]['CurrentTemperature'] = ((parseFloat(ret[i]['CurrentTemperature']) / 10) - 273.15).toFixed(2);
+            }
+        }
+    } catch (ex) { }
+    return (ret);
+}
+
+function linux_thermals()
+{
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); });
+    child.stdin.write("cat /sys/class/thermal/thermal_zone*/temp | awk '{ print $0 / 1000 }'\nexit\n");
+    child.waitExit();
+    var ret = child.stdout.str.trim().split('\n');
+    if (ret.length == 1 && ret[0] == '') { ret = []; }
+    return (ret);
+}
+
+function macos_thermals()
+{
+    var ret = [];
+    var child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stderr.on('data', function () { });
+    child.stdin.write('powermetrics --help | grep SMC\nexit\n');
+    child.waitExit();
+    
+    if (child.stdout.str.trim() != '')
+    {
+        child = require('child_process').execFile('/bin/sh', ['sh']);
+        child.stdout.str = ''; child.stdout.on('data', function (c)
+        {
+            this.str += c.toString();
+            var tokens = this.str.trim().split('\n');
+            for (var i in tokens)
+            {
+                if (tokens[i].split(' die temperature: ').length > 1)
+                {
+                    ret.push(tokens[i].split(' ')[3]);
+                    this.parent.kill();
+                }
+            }
+        });
+        child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); });
+        child.stdin.write('powermetrics -s smc\n');
+        child.waitExit(5000);
+    }
+    return (ret);
+}
+
+const platformConfig = {
+    linux: { cpuUtilization: linux_cpuUtilization, memUtilization: linux_memUtilization, thermals: linux_thermals },
+    win32: { cpuUtilization: windows_cpuUtilization, memUtilization: windows_memUtilization, thermals: windows_thermals },
+    darwin: { cpuUtilization: macos_cpuUtilization, memUtilization: macos_memUtilization, thermals: macos_thermals }
+};
+
+module.exports = platformConfig[process.platform];

+ 101 - 0
agents/modules_meshcmd/win-securitycenter.js

@@ -0,0 +1,101 @@
+/*
+Copyright 2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+var seccenter = null;
+var WSC_SECURITY_PROVIDER_FIREWALL = 0x1;
+var WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS = 0x2;
+var WSC_SECURITY_PROVIDER_ANTIVIRUS = 0x4;
+var WSC_SECURITY_PROVIDER_ANTISPYWARE = 0x8;
+
+var WSC_SECURITY_PROVIDER_HEALTH_GOOD = 0;          // Green pillar in English locales
+var WSC_SECURITY_PROVIDER_HEALTH_NOTMONITORED = 1;  // Yellow pillar in English locales
+var WSC_SECURITY_PROVIDER_HEALTH_POOR = 2;          // Red pillar in English locales
+var WSC_SECURITY_PROVIDER_HEALTH_SNOOZE = 3;        // Yellow pillar in English locales
+
+try
+{
+    seccenter = require('_GenericMarshal').CreateNativeProxy('Wscapi.dll');
+    seccenter.CreateMethod('WscGetSecurityProviderHealth');
+    seccenter.CreateMethod('WscRegisterForChanges');
+    seccenter.CreateMethod('WscUnRegisterChanges'); 
+}
+catch(e)
+{
+}
+
+function statusString(val)
+{
+    var ret = 'UNKNOWN';
+
+    switch (val)
+    {
+        case 0:
+            ret = 'OK';
+            break;
+        case 1:
+        case 3:
+            ret = 'WARNING';
+            break;
+        case 2:
+            ret = 'PROBLEM';
+            break;
+        default:
+            ret = 'UNKNOWN';
+            break;
+    }
+    return (ret);
+}
+function getStatus()
+{
+    var ret = { firewall: 'UNKNOWN', antiVirus: 'UNKNOWN', autoUpdate: 'UNKNOWN' };
+    if (seccenter != null)
+    {
+        var status = require('_GenericMarshal').CreateVariable(4);
+        if (seccenter.WscGetSecurityProviderHealth(WSC_SECURITY_PROVIDER_FIREWALL, status).Val == 0) { ret.firewall = statusString(status.toBuffer().readUInt32LE()); }
+        if (seccenter.WscGetSecurityProviderHealth(WSC_SECURITY_PROVIDER_ANTIVIRUS, status).Val == 0) { ret.antiVirus = statusString(status.toBuffer().readUInt32LE()); }
+        if (seccenter.WscGetSecurityProviderHealth(WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS, status).Val == 0) { ret.autoUpdate = statusString(status.toBuffer().readUInt32LE()); }
+    }
+    return (ret);
+}
+
+if (process.platform == 'win32' && seccenter != null)
+{
+    var j = { status: getStatus };
+    require('events').EventEmitter.call(j, true)
+        .createEvent('changed');
+    j._H = require('_GenericMarshal').CreatePointer();
+    j._EV = require('_GenericMarshal').GetGenericGlobalCallback(1);
+    j._EV.parent = j;
+    j._EV.on('GlobalCallback', function (p)
+    {
+        if (!this.ObjectToPtr_Verify(this.parent, p)) { return; } // This event is not for us
+        this.parent.emit('changed');
+    });
+    j.on('~', function ()
+    {
+        if (seccenter.WscUnRegisterChanges(this._H).Val == 0) { }
+    });
+
+    if (seccenter.WscRegisterForChanges(0, j._H, j._EV, require('_GenericMarshal').ObjectToPtr(j)).Val == 0)
+    {
+        j._H = j._H.Deref();
+    }
+    module.exports = j;
+}
+else
+{
+    throw ('win-securitycenter not supported on this platform');
+}

+ 462 - 0
agents/modules_meshcore/amt-apfclient.js

@@ -0,0 +1,462 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/**
+* @description APF/CIRA Client for Duktape
+* @author Joko Sastriawan & Ylian Saint-Hilaire
+* @copyright Intel Corporation 2020-2021
+* @license Apache-2.0
+* @version v0.0.2
+*/
+
+function CreateAPFClient(parent, args) {
+    if ((args.clientuuid == null) || (args.clientuuid.length != 36)) return null; // Require a UUID if this exact length
+
+    var obj = {};
+    obj.parent = parent;
+    obj.args = args;
+    obj.http = require('http');
+    obj.net = require('net');
+    obj.forwardClient = null;
+    obj.downlinks = {};
+    obj.pfwd_idx = 0;
+    obj.timer = null; // Keep alive timer
+
+    // obj.onChannelClosed
+    // obj.onJsonControl
+
+    // Function copied from common.js
+    function ReadInt(v, p) { return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); }; // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
+    function IntToStr(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); };
+    function hex2rstr(d) { var r = '', m = ('' + d).match(/../g), t; while (t = m.shift()) { r += String.fromCharCode('0x' + t); } return r; };
+    function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }; // Convert decimal to hex
+    function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }; // Convert a raw string to a hex string
+    function d2h(d) { return (d / 256 + 1 / 512).toString(16).substring(2, 4); }
+    function buf2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += d2h(input[i]); } return r; };
+    function Debug(str) {
+        //require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: str });
+        if (obj.parent.debug) { console.log(str); }
+    }
+    function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + "-" + g.substring(10, 12) + g.substring(8, 10) + "-" + g.substring(14, 16) + g.substring(12, 14) + "-" + g.substring(16, 20) + "-" + g.substring(20); }
+    function strToGuid(s) { s = s.replace(/-/g, ''); var ret = s.substring(6, 8) + s.substring(4, 6) + s.substring(2, 4) + s.substring(0, 2) + s.substring(10, 12) + s.substring(8, 10) + s.substring(14, 16) + s.substring(12, 14) + s.substring(16, 20) + s.substring(20); return ret; }
+    function binzerostring(len) { var res = ''; for (var l = 0; l < len; l++) { res += String.fromCharCode(0 & 0xFF); } return res; }
+
+    // CIRA state
+    var CIRASTATE = {
+        INITIAL: 0,
+        PROTOCOL_VERSION_SENT: 1,
+        AUTH_SERVICE_REQUEST_SENT: 2,
+        AUTH_REQUEST_SENT: 3,
+        PFWD_SERVICE_REQUEST_SENT: 4,
+        GLOBAL_REQUEST_SENT: 5,
+        FAILED: -1
+    }
+    obj.cirastate = CIRASTATE.INITIAL;
+
+    // REDIR state
+    var REDIR_TYPE = {
+        REDIR_UNKNOWN: 0,
+        REDIR_SOL: 1,
+        REDIR_KVM: 2,
+        REDIR_IDER: 3
+    }
+
+    // redirection start command
+    obj.RedirectStartSol = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x53, 0x4F, 0x4C, 0x20);
+    obj.RedirectStartKvm = String.fromCharCode(0x10, 0x01, 0x00, 0x00, 0x4b, 0x56, 0x4d, 0x52);
+    obj.RedirectStartIder = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x49, 0x44, 0x45, 0x52);
+
+    // Intel AMT forwarded port list for non-TLS mode
+    //var pfwd_ports = [16992, 623, 16994, 5900];
+    var pfwd_ports = [ 16992, 16993 ];
+
+    // protocol definitions
+    var APFProtocol = {
+        UNKNOWN: 0,
+        DISCONNECT: 1,
+        SERVICE_REQUEST: 5,
+        SERVICE_ACCEPT: 6,
+        USERAUTH_REQUEST: 50,
+        USERAUTH_FAILURE: 51,
+        USERAUTH_SUCCESS: 52,
+        GLOBAL_REQUEST: 80,
+        REQUEST_SUCCESS: 81,
+        REQUEST_FAILURE: 82,
+        CHANNEL_OPEN: 90,
+        CHANNEL_OPEN_CONFIRMATION: 91,
+        CHANNEL_OPEN_FAILURE: 92,
+        CHANNEL_WINDOW_ADJUST: 93,
+        CHANNEL_DATA: 94,
+        CHANNEL_CLOSE: 97,
+        PROTOCOLVERSION: 192,
+        KEEPALIVE_REQUEST: 208,
+        KEEPALIVE_REPLY: 209,
+        KEEPALIVE_OPTIONS_REQUEST: 210,
+        KEEPALIVE_OPTIONS_REPLY: 211,
+        JSON_CONTROL: 250 // This is a Mesh specific command that sends JSON to and from the MPS server.
+    }
+
+    var APFDisconnectCode = {
+        HOST_NOT_ALLOWED_TO_CONNECT: 1,
+        PROTOCOL_ERROR: 2,
+        KEY_EXCHANGE_FAILED: 3,
+        RESERVED: 4,
+        MAC_ERROR: 5,
+        COMPRESSION_ERROR: 6,
+        SERVICE_NOT_AVAILABLE: 7,
+        PROTOCOL_VERSION_NOT_SUPPORTED: 8,
+        HOST_KEY_NOT_VERIFIABLE: 9,
+        CONNECTION_LOST: 10,
+        BY_APPLICATION: 11,
+        TOO_MANY_CONNECTIONS: 12,
+        AUTH_CANCELLED_BY_USER: 13,
+        NO_MORE_AUTH_METHODS_AVAILABLE: 14,
+        INVALID_CREDENTIALS: 15,
+        CONNECTION_TIMED_OUT: 16,
+        BY_POLICY: 17,
+        TEMPORARILY_UNAVAILABLE: 18
+    }
+
+    var APFChannelOpenFailCodes = {
+        ADMINISTRATIVELY_PROHIBITED: 1,
+        CONNECT_FAILED: 2,
+        UNKNOWN_CHANNEL_TYPE: 3,
+        RESOURCE_SHORTAGE: 4,
+    }
+
+    var APFChannelOpenFailureReasonCode = {
+        AdministrativelyProhibited: 1,
+        ConnectFailed: 2,
+        UnknownChannelType: 3,
+        ResourceShortage: 4,
+    }
+
+    obj.onSecureConnect = function onSecureConnect(resp, ws, head) {
+        Debug("APF Secure WebSocket connected.");
+        //console.log(JSON.stringify(resp));                
+        obj.forwardClient.tag = { accumulator: [] };
+        obj.forwardClient.ws = ws;
+        obj.forwardClient.ws.on('end', function () {
+            Debug("APF: Connection is closing.");
+            if (obj.timer != null) { clearInterval(obj.timer); obj.timer = null; }
+            if (obj.onChannelClosed) { obj.onChannelClosed(obj); }
+        });
+
+        obj.forwardClient.ws.on('data', function (data) {
+            obj.forwardClient.tag.accumulator += hex2rstr(buf2hex(data));
+            try {
+                var len = 0;
+                do {
+                    len = ProcessData(obj.forwardClient);
+                    if (len > 0) { obj.forwardClient.tag.accumulator = obj.forwardClient.tag.accumulator.slice(len); }
+                    if (obj.cirastate == CIRASTATE.FAILED) {
+                        Debug("APF: in a failed state, destroying socket.");
+                        obj.forwardClient.ws.end();
+                    }
+                } while (len > 0);
+            } catch (ex) { Debug(ex); }
+        });
+
+        obj.forwardClient.ws.on('error', function (e) {
+            Debug("APF: Connection error, ending connecting.");
+            if (obj.timer != null) { clearInterval(obj.timer); obj.timer = null; }
+        });
+
+        obj.state = CIRASTATE.INITIAL;
+        if ((typeof obj.args.conntype == 'number') && (obj.args.conntype != 0)) {
+            SendJsonControl(obj.forwardClient.ws, { action: 'connType', value: obj.args.conntype });
+            if (obj.args.meiState != null) { SendJsonControl(obj.forwardClient.ws, { action: 'meiState', value: obj.args.meiState }); }
+        }
+        SendProtocolVersion(obj.forwardClient.ws, obj.args.clientuuid);
+        SendServiceRequest(obj.forwardClient.ws, '[email protected]');
+    }
+
+    obj.updateMeiState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'meiState', value: state }); }
+    obj.sendMeiDeactivationState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'deactivate', value: state }); }
+    obj.sendStartTlsHostConfigResponse = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'startTlsHostConfig', value: state }); }
+    obj.sendStopConfigurationResponse = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'stopConfiguration', value: state }); }
+
+    function SendJsonControl(socket, o) {
+        var data = JSON.stringify(o)
+        socket.write(String.fromCharCode(APFProtocol.JSON_CONTROL) + IntToStr(data.length) + data);
+        Debug("APF: Send JSON control: " + data);
+    }
+
+    function SendProtocolVersion(socket, uuid) {
+        var data = String.fromCharCode(APFProtocol.PROTOCOLVERSION) + IntToStr(1) + IntToStr(0) + IntToStr(0) + hex2rstr(strToGuid(uuid)) + binzerostring(64);
+        socket.write(data);
+        Debug("APF: Send protocol version 1 0 " + uuid);
+        obj.cirastate = CIRASTATE.PROTOCOL_VERSION_SENT;
+    }
+
+    function SendServiceRequest(socket, service) {
+        var data = String.fromCharCode(APFProtocol.SERVICE_REQUEST) + IntToStr(service.length) + service;
+        socket.write(data);
+        Debug("APF: Send service request " + service);
+        if (service == '[email protected]') {
+            obj.cirastate = CIRASTATE.AUTH_SERVICE_REQUEST_SENT;
+        } else if (service == '[email protected]') {
+            obj.cirastate = CIRASTATE.PFWD_SERVICE_REQUEST_SENT;
+        }
+    }
+
+    function SendUserAuthRequest(socket, user, pass) {
+        var service = "[email protected]";
+        var data = String.fromCharCode(APFProtocol.USERAUTH_REQUEST) + IntToStr(user.length) + user + IntToStr(service.length) + service;
+        //password auth
+        data += IntToStr(8) + 'password';
+        data += binzerostring(1) + IntToStr(pass.length) + pass;
+        socket.write(data);
+        Debug("APF: Send username password authentication to MPS");
+        obj.cirastate = CIRASTATE.AUTH_REQUEST_SENT;
+    }
+
+    function SendGlobalRequestPfwd(socket, amthostname, amtport) {
+        var tcpipfwd = 'tcpip-forward';
+        var data = String.fromCharCode(APFProtocol.GLOBAL_REQUEST) + IntToStr(tcpipfwd.length) + tcpipfwd + binzerostring(1, 1);
+        data += IntToStr(amthostname.length) + amthostname + IntToStr(amtport);
+        socket.write(data);
+        Debug("APF: Send tcpip-forward " + amthostname + ":" + amtport);
+        obj.cirastate = CIRASTATE.GLOBAL_REQUEST_SENT;
+    }
+
+    function SendKeepAliveRequest(socket) {
+        socket.write(String.fromCharCode(APFProtocol.KEEPALIVE_REQUEST) + IntToStr(255));
+        Debug("APF: Send keepalive request");
+    }
+
+    function SendKeepAliveReply(socket, cookie) {
+        socket.write(String.fromCharCode(APFProtocol.KEEPALIVE_REPLY) + IntToStr(cookie));
+        Debug("APF: Send keepalive reply");
+    }
+
+    function ProcessData(socket) {
+        var cmd = socket.tag.accumulator.charCodeAt(0);
+        var len = socket.tag.accumulator.length;
+        var data = socket.tag.accumulator;
+        if (len == 0) { return 0; }
+
+        // Respond to MPS according to obj.cirastate
+        switch (cmd) {
+            case APFProtocol.SERVICE_ACCEPT: {
+                var slen = ReadInt(data, 1), service = data.substring(5, 6 + slen);
+                Debug("APF: Service request to " + service + " accepted.");
+                if (service == '[email protected]') {
+                    if (obj.cirastate >= CIRASTATE.AUTH_SERVICE_REQUEST_SENT) {
+                        SendUserAuthRequest(socket.ws, obj.args.mpsuser, obj.args.mpspass);
+                    }
+                } else if (service == '[email protected]') {
+                    if (obj.cirastate >= CIRASTATE.PFWD_SERVICE_REQUEST_SENT) {
+                        SendGlobalRequestPfwd(socket.ws, obj.args.clientname, pfwd_ports[obj.pfwd_idx++]);
+                    }
+                }
+                return 5 + slen;
+            }
+            case APFProtocol.REQUEST_SUCCESS: {
+                if (len >= 5) {
+                    var port = ReadInt(data, 1);
+                    Debug("APF: Request to port forward " + port + " successful.");
+                    // iterate to pending port forward request
+                    if (obj.pfwd_idx < pfwd_ports.length) {
+                        SendGlobalRequestPfwd(socket.ws, obj.args.clientname, pfwd_ports[obj.pfwd_idx++]);
+                    } else {
+                        // no more port forward, now setup timer to send keep alive
+                        Debug("APF: Start keep alive for every " + obj.args.mpskeepalive + " ms.");
+                        obj.timer = setInterval(function () {
+                            SendKeepAliveRequest(obj.forwardClient.ws);
+                        }, obj.args.mpskeepalive);//
+                    }
+                    return 5;
+                }
+                Debug("APF: Request successful.");
+                return 1;
+            }
+            case APFProtocol.USERAUTH_SUCCESS: {
+                Debug("APF: User Authentication successful");
+                // Send Pfwd service request
+                SendServiceRequest(socket.ws, '[email protected]');
+                return 1;
+            }
+            case APFProtocol.USERAUTH_FAILURE: {
+                Debug("APF: User Authentication failed");
+                obj.cirastate = CIRASTATE.FAILED;
+                return 14;
+            }
+            case APFProtocol.KEEPALIVE_REQUEST: {
+                Debug("APF: Keep Alive Request with cookie: " + ReadInt(data, 1));
+                SendKeepAliveReply(socket.ws, ReadInt(data, 1));
+                return 5;
+            }
+            case APFProtocol.KEEPALIVE_REPLY: {
+                Debug("APF: Keep Alive Reply with cookie: " + ReadInt(data, 1));
+                return 5;
+            }
+            // Channel management
+            case APFProtocol.CHANNEL_OPEN: {
+                // Parse CHANNEL OPEN request
+                var p_res = parseChannelOpen(data);
+                Debug("APF: CHANNEL_OPEN request: " + JSON.stringify(p_res));
+                // Check if target port is in pfwd_ports
+                if (pfwd_ports.indexOf(p_res.target_port) >= 0) {
+                    // Connect socket to that port
+                    var chan = obj.net.createConnection({ host: obj.args.clientaddress, port: p_res.target_port }, function () {
+                        //require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "CHANNEL_OPEN-open" });
+                        // obj.downlinks[p_res.sender_chan].setEncoding('binary');//assume everything is binary, not interpreting
+                        SendChannelOpenConfirm(socket.ws, p_res);
+                    });
+
+                    // Setup flow control
+                    chan.maxInWindow = p_res.window_size; // Oddly, we are using the same window size as the other side.
+                    chan.curInWindow = 0;
+
+                    chan.on('data', function (ddata) {
+                        // Relay data to fordwardclient
+                        // TODO: Implement flow control
+                        SendChannelData(socket.ws, p_res.sender_chan, ddata);
+                    });
+
+                    chan.on('error', function (e) {
+                        //Debug("Downlink connection error: " + e);
+                        SendChannelOpenFailure(socket.ws, p_res);
+                    });
+
+                    chan.on('end', function () {
+                        var chan = obj.downlinks[p_res.sender_chan];
+                        if (chan != null) {
+                            Debug("Socket ends.");
+                            try { SendChannelClose(socket.ws, p_res.sender_chan); } catch (ex) { }
+                            delete obj.downlinks[p_res.sender_chan];
+                        }
+                    });
+
+                    obj.downlinks[p_res.sender_chan] = chan;
+                } else {
+                    // Not a supported port, fail the connection
+                    SendChannelOpenFailure(socket.ws, p_res);
+                }
+                return p_res.len;
+            }
+            case APFProtocol.CHANNEL_OPEN_CONFIRMATION: {
+                Debug("APF: CHANNEL_OPEN_CONFIRMATION");
+                return 17;
+            }
+            case APFProtocol.CHANNEL_CLOSE: {
+                var rcpt_chan = ReadInt(data, 1);
+                Debug("APF: CHANNEL_CLOSE: " + rcpt_chan);
+                try { obj.downlinks[rcpt_chan].end(); } catch (ex) { }
+                return 5;
+            }
+            case APFProtocol.CHANNEL_DATA: {
+                Debug("APF: CHANNEL_DATA: " + JSON.stringify(rstr2hex(data)));
+                var rcpt_chan = ReadInt(data, 1);
+                var chan_data_len = ReadInt(data, 5);
+                var chan_data = data.substring(9, 9 + chan_data_len);
+                var chan = obj.downlinks[rcpt_chan];
+                if (chan != null) {
+                    chan.curInWindow += chan_data_len;
+                    try {
+                        chan.write(Buffer.from(chan_data, 'binary'), function () {
+                            Debug("Write completed.");
+                            // If the incoming window is over half used, send an adjust.
+                            if (this.curInWindow > (this.maxInWindow / 2)) { SendChannelWindowAdjust(socket.ws, rcpt_chan, this.curInWindow); this.curInWindow = 0; }
+                        });
+                    } catch (ex) { Debug("Cannot forward data to downlink socket."); }
+                }
+                return 9 + chan_data_len;
+            }
+            case APFProtocol.CHANNEL_WINDOW_ADJUST: {
+                Debug("APF: CHANNEL_WINDOW_ADJUST");
+                return 9;
+            }
+            case APFProtocol.JSON_CONTROL: {
+                Debug("APF: JSON_CONTROL");
+                var len = ReadInt(data, 1);
+                if (obj.onJsonControl) { var o = null; try { o = JSON.parse(data.substring(5, 5 + len)); } catch (ex) { } if (o != null) { obj.onJsonControl(o); } }
+                return 5 + len;
+            }
+            default: {
+                Debug("CMD: " + cmd + " is not implemented.");
+                obj.cirastate = CIRASTATE.FAILED;
+                return 0;
+            }
+        }
+    }
+
+    function parseChannelOpen(data) {
+        var result = { cmd: APFProtocol.CHANNEL_OPEN };
+        var chan_type_slen = ReadInt(data, 1);
+        result.chan_type = data.substring(5, 5 + chan_type_slen);
+        result.sender_chan = ReadInt(data, 5 + chan_type_slen);
+        result.window_size = ReadInt(data, 9 + chan_type_slen);
+        var c_len = ReadInt(data, 17 + chan_type_slen);
+        result.target_address = data.substring(21 + chan_type_slen, 21 + chan_type_slen + c_len);
+        result.target_port = ReadInt(data, 21 + chan_type_slen + c_len);
+        var o_len = ReadInt(data, 25 + chan_type_slen + c_len);
+        result.origin_address = data.substring(29 + chan_type_slen + c_len, 29 + chan_type_slen + c_len + o_len);
+        result.origin_port = ReadInt(data, 29 + chan_type_slen + c_len + o_len);
+        result.len = 33 + chan_type_slen + c_len + o_len;
+        return result;
+    }
+
+    function SendChannelOpenFailure(socket, chan_data) {
+        socket.write(String.fromCharCode(APFProtocol.CHANNEL_OPEN_FAILURE) + IntToStr(chan_data.sender_chan) + IntToStr(2) + IntToStr(0) + IntToStr(0));
+        Debug("APF: Send ChannelOpenFailure");
+    }
+
+    function SendChannelOpenConfirm(socket, chan_data) {
+        socket.write(String.fromCharCode(APFProtocol.CHANNEL_OPEN_CONFIRMATION) + IntToStr(chan_data.sender_chan) + IntToStr(chan_data.sender_chan) + IntToStr(chan_data.window_size) + IntToStr(0xFFFFFFFF));
+        Debug("APF: Send ChannelOpenConfirmation");
+    }
+
+    function SendChannelWindowAdjust(socket, chan, size) {
+        socket.write(String.fromCharCode(APFProtocol.CHANNEL_WINDOW_ADJUST) + IntToStr(chan) + IntToStr(size));
+        Debug("APF: Send ChannelWindowAdjust, channel: " + chan + ", size: " + size);
+    }
+
+    function SendChannelData(socket, chan, data) {
+        socket.write(Buffer.concat([Buffer.from(String.fromCharCode(APFProtocol.CHANNEL_DATA) + IntToStr(chan) + IntToStr(data.length), 'binary'), data]));
+        Debug("APF: Send ChannelData: " + data.toString('hex'));
+    }
+
+    function SendChannelClose(socket, chan) {
+        socket.write(String.fromCharCode(APFProtocol.CHANNEL_CLOSE) + IntToStr(chan));
+        Debug("APF: Send ChannelClose ");
+    }
+
+    obj.connect = function () {
+        if (obj.forwardClient != null) {
+            try { obj.forwardClient.ws.end(); } catch (ex) { Debug(ex); }
+            //obj.forwardClient = null;
+        }
+        obj.cirastate = CIRASTATE.INITIAL;
+        obj.pfwd_idx = 0;
+
+        //obj.forwardClient = new obj.ws(obj.args.mpsurl, obj.tlsoptions);
+        //obj.forwardClient.on("open", obj.onSecureConnect);
+
+        var wsoptions = obj.http.parseUri(obj.args.mpsurl);
+        wsoptions.rejectUnauthorized = 0;
+        obj.forwardClient = obj.http.request(wsoptions);
+        obj.forwardClient.upgrade = obj.onSecureConnect;
+        obj.forwardClient.end(); // end request, trigger completion of HTTP request
+    }
+
+    obj.disconnect = function () { try { obj.forwardClient.ws.end(); } catch (ex) { Debug(ex); } }
+
+    return obj;
+}
+
+module.exports = CreateAPFClient; 

+ 495 - 0
agents/modules_meshcore/amt-lme.js

@@ -0,0 +1,495 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+var MemoryStream = require('MemoryStream');
+var lme_id = 0;             // Our next channel identifier
+var lme_port_offset = 0;    // Debug: Set this to "-100" to bind to 16892 & 16893 and IN_ADDRANY. This is for LMS debugging.
+var lme_bindany = false;    // If true, bind to all network interfaces, not just loopback.
+var xmlParser = null;
+try { xmlParser = require('amt-xml'); } catch (ex) { }
+
+// Documented in: https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/HTMLDocuments/MPSDocuments/Intel%20AMT%20Port%20Forwarding%20Protocol%20Reference%20Manual.pdf
+var APF_DISCONNECT = 1;
+var APF_SERVICE_REQUEST = 5;
+var APF_SERVICE_ACCEPT = 6;
+var APF_USERAUTH_REQUEST = 50;
+var APF_USERAUTH_FAILURE = 51;
+var APF_USERAUTH_SUCCESS = 52;
+var APF_GLOBAL_REQUEST = 80;
+var APF_REQUEST_SUCCESS = 81;
+var APF_REQUEST_FAILURE = 82;
+var APF_CHANNEL_OPEN = 90;
+var APF_CHANNEL_OPEN_CONFIRMATION = 91;
+var APF_CHANNEL_OPEN_FAILURE = 92;
+var APF_CHANNEL_WINDOW_ADJUST = 93;
+var APF_CHANNEL_DATA = 94;
+var APF_CHANNEL_CLOSE = 97;
+var APF_PROTOCOLVERSION = 192;
+
+
+function lme_object() {
+    this.ourId = ++lme_id;
+    this.amtId = -1;
+    this.LME_CHANNEL_STATUS = 'LME_CS_FREE';
+    this.txWindow = 0;
+    this.rxWindow = 0;
+    this.localPort = 0;
+    this.errorCount = 0;
+}
+
+function stream_bufferedWrite() {
+    var emitterUtils = require('events').inherits(this);
+    this.buffer = [];
+    this._readCheckImmediate = undefined;
+    this._ObjectID = "bufferedWriteStream";
+    // Writable Events
+    emitterUtils.createEvent('close');
+    emitterUtils.createEvent('drain');
+    emitterUtils.createEvent('error');
+    emitterUtils.createEvent('finish');
+    emitterUtils.createEvent('pipe');
+    emitterUtils.createEvent('unpipe');
+    
+    // Readable Events
+    emitterUtils.createEvent('readable');
+    this.isEmpty = function () {
+        return (this.buffer.length == 0);
+    };
+    this.isWaiting = function () {
+        return (this._readCheckImmediate == undefined);
+    };
+    this.write = function (chunk) {
+        for (var args in arguments) { if (typeof (arguments[args]) == 'function') { this.once('drain', arguments[args]); break; } }
+        var tmp = Buffer.alloc(chunk.length);
+        chunk.copy(tmp);
+        this.buffer.push({ offset: 0, data: tmp });
+        this.emit('readable');
+        return (this.buffer.length == 0 ? true : false);
+    };
+    this.read = function () {
+        var size = arguments.length == 0 ? undefined : arguments[0];
+        var bytesRead = 0;
+        var list = [];
+        while ((size == undefined || bytesRead < size) && this.buffer.length > 0) {
+            var len = this.buffer[0].data.length - this.buffer[0].offset;
+            var offset = this.buffer[0].offset;
+            
+            if (len > (size - bytesRead)) {
+                // Only reading a subset
+                list.push(this.buffer[0].data.slice(offset, offset + size - bytesRead));
+                this.buffer[0].offset += (size - bytesRead);
+                bytesRead += (size - bytesRead);
+            } else {
+                // Reading the entire thing
+                list.push(this.buffer[0].data.slice(offset));
+                bytesRead += len;
+                this.buffer.shift();
+            }
+        }
+        this._readCheckImmediate = setImmediate(function (buffered) {
+            buffered._readCheckImmediate = undefined;
+            if (buffered.buffer.length == 0) {
+                buffered.emit('drain'); // Drained
+            } else {
+                buffered.emit('readable'); // Not drained
+            }
+        }, this);
+        return (Buffer.concat(list));
+    };
+}
+
+
+function lme_heci(options) {
+    var emitterUtils = require('events').inherits(this);
+    emitterUtils.createEvent('error');
+    emitterUtils.createEvent('connect');
+    emitterUtils.createEvent('notify');
+    emitterUtils.createEvent('bind');
+    
+    this.on('newListener', function (name, func)
+    {
+        if (name == 'connect' && this._LME._connected == true) { func.call(this); }
+        if (name == 'error' && this._LME._error !=null) { func.call(this, this._LME._error); }
+    });
+    if (options != null) {
+        if (options.debug == true) { lme_port_offset = -100; } // LMS debug mode
+        if (options.bindany == true) { lme_bindany = true; } // Bind to all ports
+    }
+
+    var heci = require('heci');
+    this.INITIAL_RXWINDOW_SIZE = 4096;
+    
+    this._ObjectID = "lme";
+    this._LME = heci.create();
+    this._LME._connected = false;
+    this._LME._error = null;
+    this._LME.descriptorMetadata = "amt-lme";
+    this._LME._binded = {};
+    this._LME.LMS = this;
+    this._LME.on('error', function (e) { this._error = e; this.LMS.emit('error', e); });
+    this._LME.on('connect', function ()
+    {
+        this._connected = true;
+        this._emitConnected = false;
+        this.on('data', function (chunk) {
+            // this = HECI
+            var cmd = chunk.readUInt8(0);
+            //console.log('LME Command ' + cmd + ', ' + chunk.length + ' byte(s).');
+            
+            switch (cmd) {
+                default:
+                    console.log('Unhandled LME Command ' + cmd + ', ' + chunk.length + ' byte(s).');
+                    break;
+                case APF_SERVICE_REQUEST:
+                    var nameLen = chunk.readUInt32BE(1);
+                    var name = chunk.slice(5, nameLen + 5);
+                    //console.log("Service Request for: " + name);
+                    if (name == '[email protected]' || name == '[email protected]') {
+                        var outBuffer = Buffer.alloc(5 + nameLen);
+                        outBuffer.writeUInt8(6, 0);
+                        outBuffer.writeUInt32BE(nameLen, 1);
+                        outBuffer.write(name.toString(), 5);
+                        this.write(outBuffer);
+                        //console.log('Answering APF_SERVICE_REQUEST');
+                    } else {
+                        //console.log('UNKNOWN APF_SERVICE_REQUEST');
+                    }
+                    break;
+                case APF_GLOBAL_REQUEST:
+                    var nameLen = chunk.readUInt32BE(1);
+                    var name = chunk.slice(5, nameLen + 5).toString();
+
+                    switch (name) {
+                        case 'tcpip-forward':
+                            var len = chunk.readUInt32BE(nameLen + 6);
+                            var port = chunk.readUInt32BE(nameLen + 10 + len);
+                            //console.log("[" + chunk.length + "/" + len + "] APF_GLOBAL_REQUEST for: " + name + " on port " + port);
+                            if (this[name] == undefined) { this[name] = {}; }
+                            if (this[name][port] != null) { // Close the existing binding
+                                for (var i in this.sockets) {
+                                    var channel = this.sockets[i];
+                                    if (channel.localPort == port) { this.sockets[i].end(); delete this.sockets[i]; } // Close this socket
+                                }
+                            }
+                            if (this[name][port] == null)
+                            {
+                                try
+                                {
+                                    // Bind a new server socket if not already present
+                                    this[name][port] = require('net').createServer();
+                                    this[name][port].descriptorMetadata = 'amt-lme (port: ' + port + ')';
+                                    this[name][port].HECI = this;
+                                    if (lme_port_offset == 0) {
+                                        if (lme_bindany) {
+                                            this[name][port].listen({ port: port }); // Bind all mode
+                                        } else {
+                                            this[name][port].listen({ port: port, host: '127.0.0.1' }); // Normal mode
+                                        }
+                                    } else {
+                                        this[name][port].listen({ port: (port + lme_port_offset) }); // Debug mode
+                                    }
+                                    this[name][port].on('connection', function (socket) {
+                                        //console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort);
+                                        this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort - lme_port_offset);
+                                    });
+                                    this._binded[port] = true;
+                                    if (!this._emitConnected)
+                                    {
+                                        this._emitConnected = true;
+                                        this.LMS.emit('error', 'APF/BIND error');
+                                    }
+                                    this.LMS.emit('bind', this._binded);
+                                } catch (ex)
+                                {
+                                    console.info1(ex, 'Port ' + port);
+                                    if(!this._emitConnected)
+                                    {
+                                        this._emitConnected = true;
+                                        this.LMS.emit('error', 'APF/BIND error');
+                                    }
+                                }
+                            }
+                            var outBuffer = Buffer.alloc(5);
+                            outBuffer.writeUInt8(81, 0);
+                            outBuffer.writeUInt32BE(port, 1);
+                            this.write(outBuffer);
+                            break;
+                        case 'cancel-tcpip-forward':
+                            var outBuffer = Buffer.alloc(1);
+                            outBuffer.writeUInt8(APF_REQUEST_SUCCESS, 0);
+                            this.write(outBuffer);
+                            break;
+                        case '[email protected]':
+                            var outBuffer = Buffer.alloc(1);
+                            outBuffer.writeUInt8(APF_REQUEST_FAILURE, 0);
+                            this.write(outBuffer);
+                            break;
+                        default:
+                            //console.log("Unknown APF_GLOBAL_REQUEST for: " + name);
+                            break;
+                    }
+                    break;
+                case APF_CHANNEL_OPEN_CONFIRMATION:
+                    var rChannel = chunk.readUInt32BE(1);
+                    var sChannel = chunk.readUInt32BE(5);
+                    var wSize = chunk.readUInt32BE(9);
+                    //console.log('rChannel/' + rChannel + ', sChannel/' + sChannel + ', wSize/' + wSize);
+                    if (this.sockets[rChannel] != undefined) {
+                        this.sockets[rChannel].lme.amtId = sChannel;
+                        this.sockets[rChannel].lme.rxWindow = wSize;
+                        this.sockets[rChannel].lme.txWindow = wSize;
+                        this.sockets[rChannel].lme.LME_CHANNEL_STATUS = 'LME_CS_CONNECTED';
+                        //console.log('LME_CS_CONNECTED');
+                        this.sockets[rChannel].bufferedStream = new stream_bufferedWrite();
+                        this.sockets[rChannel].bufferedStream.socket = this.sockets[rChannel];
+                        this.sockets[rChannel].bufferedStream.on('readable', function () {
+                            if (this.socket.lme.txWindow > 0) {
+                                var buffer = this.read(this.socket.lme.txWindow);
+                                var packet = Buffer.alloc(9 + buffer.length);
+                                packet.writeUInt8(APF_CHANNEL_DATA, 0);
+                                packet.writeUInt32BE(this.socket.lme.amtId, 1);
+                                packet.writeUInt32BE(buffer.length, 5);
+                                buffer.copy(packet, 9);
+                                this.socket.lme.txWindow -= buffer.length;
+                                this.socket.HECI.write(packet);
+                            }
+                        });
+                        this.sockets[rChannel].bufferedStream.on('drain', function () {
+                            this.socket.resume();
+                        });
+                        this.sockets[rChannel].on('data', function (chunk) {
+                            if (!this.bufferedStream.write(chunk)) { this.pause(); }
+                        });
+                        this.sockets[rChannel].on('end', function () {
+                            var outBuffer = Buffer.alloc(5);
+                            outBuffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
+                            outBuffer.writeUInt32BE(this.lme.amtId, 1);
+                            this.HECI.write(outBuffer);
+                        });
+                        this.sockets[rChannel].resume();
+                    }
+                    
+                    break;
+                case APF_PROTOCOLVERSION:
+                    var major = chunk.readUInt32BE(1);
+                    var minor = chunk.readUInt32BE(5);
+                    var reason = chunk.readUInt32BE(9);
+                    var outBuffer = Buffer.alloc(93);
+                    outBuffer.writeUInt8(192, 0);
+                    outBuffer.writeUInt32BE(1, 1);
+                    outBuffer.writeUInt32BE(0, 5);
+                    outBuffer.writeUInt32BE(reason, 9);
+                    //console.log('Answering PROTOCOL_VERSION');
+                    this.write(outBuffer);
+                    break;
+                case APF_CHANNEL_WINDOW_ADJUST:
+                    var rChannelId = chunk.readUInt32BE(1);
+                    var bytesToAdd = chunk.readUInt32BE(5);
+                    if (this.sockets[rChannelId] != undefined) {
+                        this.sockets[rChannelId].lme.txWindow += bytesToAdd;
+                        if (!this.sockets[rChannelId].bufferedStream.isEmpty() && this.sockets[rChannelId].bufferedStream.isWaiting()) {
+                            this.sockets[rChannelId].bufferedStream.emit('readable');
+                        }
+                    } else {
+                        console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_WINDOW_ADJUST');
+                    }
+                    break;
+                case APF_CHANNEL_DATA:
+                    var rChannelId = chunk.readUInt32BE(1);
+                    var dataLen = chunk.readUInt32BE(5);
+                    var data = chunk.slice(9, 9 + dataLen);
+                    if ((this.sockets != null) && (this.sockets[rChannelId] != undefined)) {
+                        this.sockets[rChannelId].pendingBytes.push(data.length);
+                        this.sockets[rChannelId].write(data, function () {
+                            var written = this.pendingBytes.shift();
+                            //console.log('adjust', this.lme.amtId, written);
+                            var outBuffer = Buffer.alloc(9);
+                            outBuffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0);
+                            outBuffer.writeUInt32BE(this.lme.amtId, 1);
+                            outBuffer.writeUInt32BE(written, 5);
+                            this.HECI.write(outBuffer);
+                        });
+                    } else if ((this.insockets != null) && (this.insockets[rChannelId] != undefined)) {
+                        var channel = this.insockets[rChannelId];
+                        if (channel.data == null) { channel.data = data.toString(); } else { channel.data += data.toString(); }
+                        channel.rxWindow += dataLen;
+                        //console.log('IN DATA', channel.rxWindow, channel.data.length, dataLen, channel.amtId, data.toString());
+                        var httpData = parseHttp(channel.data);
+                        if ((httpData != null) || (channel.data.length >= 8000)) {
+                            // Parse the WSMAN
+                            var notify = null;
+                            if (xmlParser != null) { try { notify = xmlParser.ParseWsman(httpData); } catch (e) { } }
+
+                            // Event the http data
+                            if (notify != null) { this.LMS.emit('notify', notify, channel.options, _lmsNotifyToCode(notify)); }
+
+                            // Send channel close
+                            var buffer = Buffer.alloc(5);
+                            buffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
+                            buffer.writeUInt32BE(amtId, 1);
+                            this.write(buffer);
+                        } else {
+                            if (channel.rxWindow > 6000) {
+                                // Send window adjust
+                                var buffer = Buffer.alloc(9);
+                                buffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0);
+                                buffer.writeUInt32BE(channel.amtId, 1);
+                                buffer.writeUInt32BE(channel.rxWindow, 5);
+                                this.write(buffer);
+                                channel.rxWindow = 0;
+                            }
+                        }
+                    } else {
+                        console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_DATA');
+                    }
+                    break;
+                case APF_CHANNEL_OPEN_FAILURE:
+                    var rChannelId = chunk.readUInt32BE(1);
+                    var reasonCode = chunk.readUInt32BE(5);
+                    if ((this.sockets != null) && (this.sockets[rChannelId] != undefined)) {
+                        this.sockets[rChannelId].end();
+                        delete this.sockets[rChannelId];
+                    } else if ((this.insockets != null) && (this.insockets[rChannelId] != undefined)) {
+                        delete this.insockets[rChannelId];
+                    } else {
+                        console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_OPEN_FAILURE');
+                    }
+                    break;
+                case APF_CHANNEL_CLOSE:
+                    var rChannelId = chunk.readUInt32BE(1);
+                    if ((this.sockets != null) && (this.sockets[rChannelId] != undefined)) {
+                        this.sockets[rChannelId].end();
+                        var amtId = this.sockets[rChannelId].lme.amtId;
+                        var buffer = Buffer.alloc(5);
+                        delete this.sockets[rChannelId];
+                        
+                        buffer.writeUInt8(APF_CHANNEL_CLOSE, 0); // ????????????????????????????
+                        buffer.writeUInt32BE(amtId, 1);
+                        this.write(buffer);
+                    } else if ((this.insockets != null) && (this.insockets[rChannelId] != undefined)) {
+                        delete this.insockets[rChannelId];
+                        // Should I send a close back????
+                    } else {
+                        console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_CLOSE');
+                    }
+                    break;
+                case APF_CHANNEL_OPEN:
+                    var nameLen = chunk.readUInt32BE(1);
+                    var name = chunk.slice(5, nameLen + 5).toString();
+                    var channelSender = chunk.readUInt32BE(nameLen + 5);
+                    var initialWindowSize = chunk.readUInt32BE(nameLen + 9);
+                    var hostToConnectLen = chunk.readUInt32BE(nameLen + 17);
+                    var hostToConnect = chunk.slice(nameLen + 21, nameLen + 21 + hostToConnectLen).toString();
+                    var portToConnect = chunk.readUInt32BE(nameLen + 21 + hostToConnectLen);
+                    var originatorIpLen = chunk.readUInt32BE(nameLen + 25 + hostToConnectLen);
+                    var originatorIp = chunk.slice(nameLen + 29 + hostToConnectLen, nameLen + 29 + hostToConnectLen + originatorIpLen).toString();
+                    var originatorPort = chunk.readUInt32BE(nameLen + 29 + hostToConnectLen + originatorIpLen);
+                    //console.log('APF_CHANNEL_OPEN', name, channelSender, initialWindowSize, 'From: ' + originatorIp + ':' + originatorPort, 'To: ' + hostToConnect + ':' + portToConnect);
+
+                    if (this.insockets == null) { this.insockets = {}; }
+                    var ourId = ++lme_id;
+                    var insocket = new lme_object();
+                    insocket.ourId = ourId;
+                    insocket.amtId = channelSender;
+                    insocket.txWindow = initialWindowSize;
+                    insocket.rxWindow = 0;
+                    insocket.options = { target: hostToConnect, targetPort: portToConnect, source: originatorIp, sourcePort: originatorPort };
+                    this.insockets[ourId] = insocket;
+
+                    var buffer = Buffer.alloc(17);
+                    buffer.writeUInt8(APF_CHANNEL_OPEN_CONFIRMATION, 0);
+                    buffer.writeUInt32BE(channelSender, 1);     // Intel AMT sender channel
+                    buffer.writeUInt32BE(ourId, 5);             // Our receiver channel id
+                    buffer.writeUInt32BE(4000, 9);              // Initial Window Size
+                    buffer.writeUInt32BE(0xFFFFFFFF, 13);       // Reserved
+                    this.write(buffer);
+
+                    /*
+                    var buffer = Buffer.alloc(17);
+                    buffer.writeUInt8(APF_CHANNEL_OPEN_FAILURE, 0);
+                    buffer.writeUInt32BE(channelSender, 1);     // Intel AMT sender channel
+                    buffer.writeUInt32BE(2, 5);                 // Reason code
+                    buffer.writeUInt32BE(0, 9);                 // Reserved
+                    buffer.writeUInt32BE(0, 13);                // Reserved
+                    this.write(buffer);
+                    console.log('Sent APF_CHANNEL_OPEN_FAILURE', channelSender);
+                    */
+
+                    break;
+            }
+        });
+        //
+        // Due to a change in behavior with AMT/11 (and possibly earlier), we are not going to emit 'connect' here, until
+        // we can verify that the first APF/Channel can be bound. Older AMT, like AMT/7 only allowed a single LME connection, so we
+        // used to emit connect here. However, newer AMT's will allow more than 1 LME connection, which will result in APF/Bind failure
+        //
+        //this.LMS.emit('connect');
+        this.resume();
+
+    });
+    
+    this.bindDuplexStream = function (duplexStream, remoteFamily, localPort) {
+        var socket = duplexStream;
+        //console.log('New [' + remoteFamily + '] Virtual Connection/' + socket.localPort);
+        socket.pendingBytes = [];
+        socket.HECI = this._LME;
+        socket.LMS = this;
+        socket.lme = new lme_object();
+        socket.lme.Socket = socket;
+        socket.localPort = localPort;
+        var buffer = new MemoryStream();
+        buffer.writeUInt8(0x5A);
+        buffer.writeUInt32BE(15);
+        buffer.write('forwarded-tcpip');
+        buffer.writeUInt32BE(socket.lme.ourId);
+        buffer.writeUInt32BE(this.INITIAL_RXWINDOW_SIZE);
+        buffer.writeUInt32BE(0xFFFFFFFF);
+        for (var i = 0; i < 2; ++i) {
+            if (remoteFamily == 'IPv6') {
+                buffer.writeUInt32BE(3);
+                buffer.write('::1');
+            } else {
+                buffer.writeUInt32BE(9);
+                buffer.write('127.0.0.1');
+            }
+            buffer.writeUInt32BE(localPort);
+        }
+        this._LME.write(buffer.buffer);
+        if (this._LME.sockets == undefined) { this._LME.sockets = {}; }
+        this._LME.sockets[socket.lme.ourId] = socket;
+        socket.pause();
+    };
+    
+    this._LME.connect(heci.GUIDS.LME, { noPipeline: 0 });
+}
+
+function parseHttp(httpData) {
+    var i = httpData.indexOf('\r\n\r\n');
+    if ((i == -1) || (httpData.length < (i + 2))) { return null; }
+    var headers = require('http-headers')(httpData.substring(0, i), true);
+    var contentLength = parseInt(headers['content-length']);
+    if (httpData.length >= contentLength + i + 4) { return httpData.substring(i + 4, i + 4 + contentLength); }
+    return null;
+}
+
+function _lmsNotifyToCode(notify) {
+    if ((notify == null) || (notify.Body == null) || (notify.Body.MessageID == null)) return null;
+    var msgid = notify.Body.MessageID;
+    try { msgid += '-' + notify.Body.MessageArguments[0]; } catch (e) { }
+    return msgid;
+}
+
+module.exports = lme_heci;

+ 168 - 0
agents/modules_meshcore/amt-manage.js

@@ -0,0 +1,168 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/**
+* @fileoverview Intel(r) AMT Management
+* @author Ylian Saint-Hilaire
+* @version v0.1.0
+*/
+
+/**
+ * Construct a AmtStackCreateService object, this ia the main Intel AMT communication stack.
+ * @constructor
+ */
+function AmtManager(agent, db, isdebug) {
+    var sendConsole = function (msg) { agent.SendCommand({ 'action': 'msg', 'type': 'console', 'value': msg }); }
+    var debug = function (msg) { if (isdebug) { sendConsole('amt-manager: ' + msg + '<br />'); } }
+    var amtMei = null, amtMeiState = 0;
+    var amtLms = null, amtLmsState = 0;
+    var amtGetVersionResult = null;
+    var obj = this;
+    var mestate;
+    var trustedHashes = null;;
+
+    require('events').EventEmitter.call(obj, true)
+        .createEvent('stateChange_LMS')
+        .createEvent('portBinding_LMS');
+    obj._lmsstate = 0;
+    obj._mapping = [];
+
+    obj.on('newListener', function (name, callback) {
+        if (name == 'portBinding_LMS') { callback.call(this, this._mapping); }
+    });
+
+    Object.defineProperty(obj, 'lmsstate',
+        {
+            get: function () { return (this._lmsstate); },
+            set: function (value) { if (this._lmsstate != value) { this._lmsstate = value; this.emit('stateChange_LMS', value); } }
+        });
+
+    obj.state = 0;
+    obj.onStateChange = null;
+    obj.setDebug = function (x) { isdebug = x; }
+
+    // Try to load up the MEI module
+    var rebindToMeiRetrys = 0;
+    obj.reset = function () {
+        ++rebindToMeiRetrys;
+        obj.amtMei = null, amtMei = null, amtMeiState = 0, amtLms = null, amtLmsState = 0, obj.state = 0, obj.lmsstate = 0;
+        //debug('Binding to MEI');
+        try {
+            var amtMeiLib = require('amt-mei');
+            obj.amtMei = amtMei = new amtMeiLib();
+            amtMei.on('error', function (e) { debug('MEI error'); amtMei = null; amtMeiState = -1; obj.state = -1; if (obj.onStateChange != null) { obj.onStateChange(amtMeiState); } });
+            amtMei.getVersion(function (result) {
+                if (result == null) {
+                    obj.state = amtMeiState = -1;
+                    if (obj.onStateChange != null) { obj.onStateChange(amtMeiState); }
+                    if (rebindToMeiRetrys < 10) { setTimeout(obj.reset, 10000); }
+                } else {
+                    amtGetVersionResult = result;
+                    obj.state = amtMeiState = 2;
+                    rebindToMeiRetrys = 0;
+                    if (obj.onStateChange != null) { obj.onStateChange(amtMeiState); }
+                    //debug('MEI binded');
+                    obj.lmsreset();
+                }
+            });
+        } catch (ex) { debug("MEI exception: " + ex); amtMei = null; amtMeiState = -1; obj.state = -1; }
+    }
+
+    // Get Intel MEI State in a flexible way
+    // Flags: 1 = Versions, 2 = OsAdmin, 4 = Hashes, 8 = Network
+    var getMeiStateCache = {}; // Some MEI calls will only be made once and cached here.
+    obj.getMeiState = function(flags, func) {
+        if ((amtMei == null) || (amtMeiState < 2)) { if (func != null) { func(null); } return; }
+        try {
+            var amtMeiTmpState = { 'core-ver': 1, OsHostname: require('os').hostname(), Flags: 0 }; // Flags: 1=EHBC, 2=CCM, 4=ACM
+            if (getMeiStateCache.MeiVersion != null) { amtMeiTmpState.MeiVersion = getMeiStateCache.MeiVersion; } else { amtMei.getProtocolVersion(function (result) { if (result != null) { getMeiStateCache.MeiVersion = amtMeiTmpState.MeiVersion = result; } }); }
+            if ((flags & 1) != 0) {
+                if (getMeiStateCache.Versions != null) {
+                    amtMeiTmpState.Versions = getMeiStateCache.Versions;
+                } else {
+                    amtMei.getVersion(function (result) { if (result) { getMeiStateCache.Versions = amtMeiTmpState.Versions = {}; for (var version in result.Versions) { amtMeiTmpState.Versions[result.Versions[version].Description] = result.Versions[version].Version; } } });
+                }
+            }
+            amtMei.getProvisioningMode(function (result) { if (result) { amtMeiTmpState.ProvisioningMode = result.mode; } });
+            amtMei.getProvisioningState(function (result) { if (result) { amtMeiTmpState.ProvisioningState = result.state; if (result.state != 2) { amtMei.stopConfiguration(function () { }); } } }); // 0: "Not Activated (Pre)", 1: "Not Activated (In)", 2: "Activated". Make sure to stop remote configuration if needed.
+            amtMei.getEHBCState(function (result) { if ((result != null) && (result.EHBC == true)) { amtMeiTmpState.Flags += 1; } });
+            amtMei.getControlMode(function (result) { if (result != null) { if (result.controlMode == 1) { amtMeiTmpState.Flags += 2; } if (result.controlMode == 2) { amtMeiTmpState.Flags += 4; } } }); // Flag 2 = CCM, 4 = ACM
+            //amtMei.getMACAddresses(function (result) { if (result) { amtMeiTmpState.mac = result; } });
+            if ((flags & 8) != 0) {
+                amtMei.getLanInterfaceSettings(0, function (result) {
+                    if (result) {
+                        amtMeiTmpState.net0 = result;
+                        var fqdn = null, interfaces = require('os').networkInterfaces(); // Look for the DNS suffix for the Intel AMT Ethernet interface
+                        for (var i in interfaces) { for (var j in interfaces[i]) { if ((interfaces[i][j].mac == result.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { amtMeiTmpState.OsDnsSuffix = interfaces[i][j].fqdn; } } }
+                    }
+                });
+                amtMei.getLanInterfaceSettings(1, function (result) { if (result) { amtMeiTmpState.net1 = result; } });
+            }
+            if (getMeiStateCache.UUID != null) { amtMeiTmpState.UUID = getMeiStateCache.UUID; } else { amtMei.getUuid(function (result) { if ((result != null) && (result.uuid != null)) { getMeiStateCache.UUID = amtMeiTmpState.UUID = result.uuid; } }); }
+            if ((flags & 2) != 0) { amtMei.getLocalSystemAccount(function (x) { if ((x != null) && x.user && x.pass) { amtMeiTmpState.OsAdmin = { user: x.user, pass: x.pass }; } }); }
+            amtMei.getDnsSuffix(function (result) { if (result != null) { amtMeiTmpState.DnsSuffix = result; } if ((flags & 4) == 0) { if (func != null) { func(amtMeiTmpState); } } });
+            if ((flags & 4) != 0) {
+                amtMei.getHashHandles(function (handles) {
+                    if ((handles != null) && (handles.length > 0)) { amtMeiTmpState.Hashes = []; } else { func(amtMeiTmpState); }
+                    var exitOnCount = handles.length;
+                    for (var i = 0; i < handles.length; ++i) { this.getCertHashEntry(handles[i], function (hashresult) { amtMeiTmpState.Hashes.push(hashresult); if (--exitOnCount == 0) { if (func != null) { func(amtMeiTmpState); } } }); }
+                });
+            }
+        } catch (e) { if (func != null) { func(null); } return; }
+    }
+
+    // Called on MicroLMS Intel AMT user notification
+    var handleAmtNotification = function (notifyMsg) {
+        if ((notifyMsg == null) || (notifyMsg.Body == null) || (notifyMsg.Body.MessageID == null) || (notifyMsg.Body.MessageArguments == null)) return null;
+        var amtMessage = notifyMsg.Body.MessageID, amtMessageArg = notifyMsg.Body.MessageArguments[0], notify = null;
+
+        switch (amtMessage) {
+            case 'iAMT0050': { if (amtMessageArg == '48') { notify = "Intel&reg; AMT Serial-over-LAN connected"; } else if (amtMessageArg == '49') { notify = "Intel&reg; AMT Serial-over-LAN disconnected"; } break; } // SOL
+            case 'iAMT0052': { if (amtMessageArg == '1') { notify = "Intel&reg; AMT KVM connected"; } else if (amtMessageArg == '2') { notify = "Intel&reg; AMT KVM disconnected"; } break; } // KVM
+            default: { break; }
+        }
+
+        // Sent to the entire group, no sessionid or userid specified.
+        if (notify != null) { agent.SendCommand({ 'action': 'msg', 'type': 'notify', 'value': notify, 'tag': 'general', 'amtMessage': amtMessage }); }
+    }
+
+    // Launch LMS
+    obj.lmsreset = function () {
+        //debug('Binding to LMS');
+        obj.lmsstate = 0;
+        try {
+            var lme_heci = require('amt-lme');
+            obj.lmsstate = amtLmsState = 1;
+            amtLms = new lme_heci();
+            amtLms.on('error', function (e) { amtLmsState = 0; obj.lmsstate = 0; amtLms = null; debug("LMS error: " + e); });
+            amtLms.on('connect', function () { amtLmsState = 2; obj.lmsstate = 2; debug("LMS connected"); });
+            amtLms.on('bind', function (map) { obj._mapping = map; obj.emit('portBinding_LMS', map); });
+            amtLms.on('notify', function (data, options, code) { handleAmtNotification(data); });
+        } catch (ex) {
+            require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "ex: " + ex });
+            amtLmsState = -1; obj.lmsstate = -1; amtLms = null;
+        }
+    }
+
+    // Start host based ACM activation with TLS
+    obj.startConfigurationHBased = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
+        if ((amtMei == null) || (amtMeiState < 2)) { if (func != null) { func({ status: -100 }); } return; }
+        amtMei.startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func);
+    }
+
+}
+
+module.exports = AmtManager;

+ 499 - 0
agents/modules_meshcore/amt-mei.js

@@ -0,0 +1,499 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+var Q = require('queue');
+var g_internal = null;
+
+function retry_pthi_later()
+{
+    if (++g_internal.errorCount < 20)
+    {
+        g_internal.timeout = setTimeout(function (p)
+        {
+            p.connect(require('heci').GUIDS.AMT, { noPipeline: 1 });
+        }, 250, this);
+    }
+    else
+    {
+        this.Parent.emit('error', 'PTHI Connection could not be established'); 
+    }
+}
+
+function amt_heci()
+{
+    var emitterUtils = require('events').inherits(this);
+    emitterUtils.createEvent('error');
+
+    var heci = require('heci');
+    var sendConsole = function (msg) { try { require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": msg }); } catch (ex) { } }
+
+    this._ObjectID = "pthi";
+    var that = this;
+    if (g_internal == null)
+    {
+        g_internal = { _rq: new Q(), _amt: null, errorCount: 0 };
+        g_internal._setupPTHI = function _g_setupPTHI()
+        {
+            console.info1('setupPTHI()');
+            this._amt = heci.create();
+            this._amt.descriptorMetadata = "amt-pthi";
+            this._amt.BiosVersionLen = 65;
+            this._amt.UnicodeStringLen = 20;
+            this._amt.Parent = that;
+
+            this._amt.on('error', function _amtOnError(e)
+            {
+                console.info1('PTHIError: ' + e);
+                if (g_internal._rq.isEmpty())
+                {
+                    console.info1(' Queue is empty');
+                    this.Parent.emit('error', e); // No pending requests, so propagate the error up
+                }
+                else
+                {
+                    console.info1(' Queue is NOT empty');
+
+                    // Try again
+                    retry_pthi_later.call(this);
+                }
+            });
+            this._amt.on('connect', function _amtOnConnect()
+            {
+                g_internal.errorCount = 0;
+                this.on('data', function _amtOnData(chunk)
+                {
+                    //console.log("Received: " + chunk.length + " bytes");
+                    var header = this.Parent.getCommand(chunk);
+                    console.info1("CMD = " + header.Command + " (Status: " + header.Status + ") Response = " + header.IsResponse);
+
+                    var user = g_internal._rq.deQueue();
+                    var params = user.optional;
+                    var callback = user.func;
+
+                    params.unshift(header);
+                    callback.apply(this.Parent, params);
+
+                    if (g_internal._rq.isEmpty())
+                    {
+                        console.info1('No more requests, disconnecting');
+
+                        // No More Requests, we can close PTHI
+                        g_internal._amt.disconnect();
+                        g_internal._amt = null;
+                    }
+                    else
+                    {
+                        // Send the next request
+                        console.info1('Sending Next Request');
+                        this.write(g_internal._rq.peekQueue().send);
+                    }
+                });
+
+                // Start sending requests
+                this.write(g_internal._rq.peekQueue().send);
+            });
+
+
+
+        };
+    }
+
+    
+    function trim(x) { var y = x.indexOf('\0'); if (y >= 0) { return x.substring(0, y); } else { return x; } }
+    this.getCommand = function getCommand(chunk) {
+        var command = chunk.length == 0 ? (g_internal._rq.peekQueue().cmd | 0x800000) : chunk.readUInt32LE(4);
+        var ret = { IsResponse: (command & 0x800000) == 0x800000 ? true : false, Command: (command & 0x7FFFFF), Status: chunk.length != 0 ? chunk.readUInt32LE(12) : -1, Data: chunk.length != 0 ? chunk.slice(16) : null };
+        return (ret);
+    };
+
+    this.sendCommand = function sendCommand()
+    {
+        if (arguments.length < 3 || typeof (arguments[0]) != 'number' || typeof (arguments[1]) != 'object' || typeof (arguments[2]) != 'function') { throw ('invalid parameters'); }
+        var args = [];
+        for (var i = 3; i < arguments.length; ++i) { args.push(arguments[i]); }
+
+        console.info1('sendCommand(' + arguments[0] + ')', this._hashCode());
+
+        var header = Buffer.from('010100000000000000000000', 'hex');
+        header.writeUInt32LE(arguments[0] | 0x04000000, 4);
+        header.writeUInt32LE(arguments[1] == null ? 0 : arguments[1].length, 8);
+
+        g_internal._rq.enQueue({ cmd: arguments[0], func: arguments[2], optional: args, send: (arguments[1] == null ? header : Buffer.concat([header, arguments[1]])) });
+        if (!g_internal._amt)
+        {
+            g_internal._setupPTHI();
+            g_internal._amt.connect(heci.GUIDS.AMT, { noPipeline: 1 });
+        }
+    }
+
+    this.getVersion = function getVersion(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(26, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var i, CodeVersion = header.Data, val = { BiosVersion: CodeVersion.slice(0, g_internal._amt.BiosVersionLen).toString(), Versions: [] }, v = CodeVersion.slice(g_internal._amt.BiosVersionLen + 4);
+                for (i = 0; i < CodeVersion.readUInt32LE(g_internal._amt.BiosVersionLen) ; ++i)
+                {
+                    val.Versions[i] = { Description: v.slice(2, v.readUInt16LE(0) + 2).toString(), Version: v.slice(4 + g_internal._amt.UnicodeStringLen, 4 + g_internal._amt.UnicodeStringLen + v.readUInt16LE(2 + g_internal._amt.UnicodeStringLen)).toString() };
+                    v = v.slice(4 + (2 * g_internal._amt.UnicodeStringLen));
+                }
+                if (val.BiosVersion.indexOf('\0') > 0) { val.BiosVersion = val.BiosVersion.substring(0, val.BiosVersion.indexOf('\0')); }
+                opt.unshift(val);
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+
+    // Fill the left with zeros until the string is of a given length
+    function zeroLeftPad(str, len) {
+        if ((len == null) && (typeof (len) != 'number')) { return null; }
+        if (str == null) str = ''; // If null, this is to generate zero leftpad string
+        var zlp = '';
+        for (var i = 0; i < len - str.length; i++) { zlp += '0'; }
+        return zlp + str;
+    }
+
+    this.getUuid = function getUuid(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x5c, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var result = {};
+                result.uuid = [zeroLeftPad(header.Data.readUInt32LE(0).toString(16), 8),
+                    zeroLeftPad(header.Data.readUInt16LE(4).toString(16), 4),
+                    zeroLeftPad(header.Data.readUInt16LE(6).toString(16), 4),
+                    zeroLeftPad(header.Data.readUInt16BE(8).toString(16), 4),
+                    zeroLeftPad(header.Data.slice(10).toString('hex').toLowerCase(), 12)].join('-');
+                opt.unshift(result);
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+
+    this.getProvisioningState = function getProvisioningState(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(17, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var result = {};
+                result.state = header.Data.readUInt32LE(0);
+                if (result.state < 3) { result.stateStr = ["PRE", "IN", "POST"][result.state]; }
+                opt.unshift(result);
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getProvisioningMode = function getProvisioningMode(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(8, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var result = {};
+                result.mode = header.Data.readUInt32LE(0);
+                if (result.mode < 4) { result.modeStr = ["NONE", "ENTERPRISE", "SMALL_BUSINESS", "REMOTE_ASSISTANCE"][result.mode]; }
+                result.legacy = header.Data.readUInt32LE(4) == 0 ? false : true;
+                opt.unshift(result);
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getEHBCState = function getEHBCState(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(132, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                opt.unshift({ EHBC: header.Data.readUInt32LE(0) != 0 });
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getControlMode = function getControlMode(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(107, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var result = {};
+                result.controlMode = header.Data.readUInt32LE(0);
+                if (result.controlMode < 3) { result.controlModeStr = ["NONE_RPAT", "CLIENT", "ADMIN", "REMOTE_ASSISTANCE"][result.controlMode]; }
+                opt.unshift(result);
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getMACAddresses = function getMACAddresses(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(37, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                opt.unshift({ DedicatedMAC: header.Data.slice(0, 6).toString('hex:'), HostMAC: header.Data.slice(6, 12).toString('hex:') });
+            } else { opt.unshift({ DedicatedMAC: null, HostMAC: null }); }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getDnsSuffix = function getDnsSuffix(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(54, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var resultLen = header.Data.readUInt16LE(0);
+                if (resultLen > 0) { opt.unshift(header.Data.slice(2, 2 + resultLen).toString()); } else { opt.unshift(null); }
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getHashHandles = function getHashHandles(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x2C, null, function (header, fn, opt) {
+            var result = [];
+            if (header.Status == 0) {
+                var resultLen = header.Data.readUInt32LE(0);
+                for (var i = 0; i < resultLen; ++i) {
+                    result.push(header.Data.readUInt32LE(4 + (4 * i)));
+                }
+            }
+            opt.unshift(result);
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getCertHashEntry = function getCertHashEntry(handle, callback) {
+        var optional = [];
+        for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
+
+        var data = Buffer.alloc(4);
+        data.writeUInt32LE(handle, 0);
+
+        this.sendCommand(0x2D, data, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var result = {};
+                result.isDefault = header.Data.readUInt32LE(0);
+                result.isActive = header.Data.readUInt32LE(4);
+                result.hashAlgorithm = header.Data.readUInt8(72);
+                if (result.hashAlgorithm < 4) {
+                    result.hashAlgorithmStr = ["MD5", "SHA1", "SHA256", "SHA512"][result.hashAlgorithm];
+                    result.hashAlgorithmSize = [16, 20, 32, 64][result.hashAlgorithm];
+                    result.certificateHash = header.Data.slice(8, 8 + result.hashAlgorithmSize).toString('hex');
+                }
+                result.name = header.Data.slice(73 + 2, 73 + 2 + header.Data.readUInt16LE(73)).toString();
+                opt.unshift(result);
+            } else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    };
+    this.getCertHashEntries = function getCertHashEntries(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+
+        this.getHashHandles(function (handles, fn, opt) {
+            var entries = [];
+            this.getCertHashEntry(handles.shift(), this._getHashEntrySink, fn, opt, entries, handles);
+        }, callback, optional);
+    };
+
+    this._getHashEntrySink = function _getHashEntrySink(result, fn, opt, entries, handles) {
+        entries.push(result);
+        if (handles.length > 0) {
+            this.getCertHashEntry(handles.shift(), this._getHashEntrySink, fn, opt, entries, handles);
+        } else {
+            opt.unshift(entries);
+            fn.apply(this, opt);
+        }
+    }
+    this.getLocalSystemAccount = function getLocalSystemAccount(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(103, Buffer.alloc(40), function (header, fn, opt) {
+            if (header.Status == 0 && header.Data.length == 68) {
+                opt.unshift({ user: trim(header.Data.slice(0, 33).toString()), pass: trim(header.Data.slice(33, 67).toString()), raw: header.Data });
+            }
+            else {
+                opt.unshift(null);
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    }
+    this.getLanInterfaceSettings = function getLanInterfaceSettings(index, callback) {
+        var optional = [];
+        for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        var ifx = Buffer.alloc(4);
+        ifx.writeUInt32LE(index);
+        this.sendCommand(0x48, ifx, function onGetLanInterfaceSettings(header, fn, opt) {
+            if (header.Status == 0) {
+                var info = {};
+                info.enabled = header.Data.readUInt32LE(0);
+                info.dhcpEnabled = header.Data.readUInt32LE(8);
+                switch (header.Data[12]) {
+                    case 1:
+                        info.dhcpMode = 'ACTIVE'
+                        break;
+                    case 2:
+                        info.dhcpMode = 'PASSIVE'
+                        break;
+                    default:
+                        info.dhcpMode = 'UNKNOWN';
+                        break;
+                }
+                info.mac = header.Data.slice(14).toString('hex:');
+
+                var addr = header.Data.readUInt32LE(4);
+                info.address = ((addr >> 24) & 255) + '.' + ((addr >> 16) & 255) + '.' + ((addr >> 8) & 255) + '.' + (addr & 255);
+                opt.unshift(info);
+                fn.apply(this, opt);
+            }
+            else {
+                opt.unshift(null);
+                fn.apply(this, opt);
+            }
+        }, callback, optional);
+
+    };
+    this.unprovision = function unprovision(mode, callback) {
+        var optional = [];
+        for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        var data = Buffer.alloc(4);
+        data.writeUInt32LE(mode, 0);
+        this.sendCommand(16, data, function (header, fn, opt) {
+            opt.unshift(header.Status);
+            fn.apply(this, opt);
+        }, callback, optional);
+    }
+    this.startConfiguration = function startConfiguration(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x29, null, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
+    }
+    this.stopConfiguration = function stopConfiguration(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x5E, null, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
+    }
+    this.openUserInitiatedConnection = function openUserInitiatedConnection(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x44, null, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
+    }
+    this.closeUserInitiatedConnection = function closeUnserInitiatedConnected(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x45, null, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional);
+    }
+    this.getRemoteAccessConnectionStatus = function getRemoteAccessConnectionStatus(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); }
+        this.sendCommand(0x46, null, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var hostname = header.Data.slice(14, header.Data.readUInt16LE(12) + 14).toString()
+                opt.unshift({ status: header.Status, networkStatus: header.Data.readUInt32LE(0), remoteAccessStatus: header.Data.readUInt32LE(4), remoteAccessTrigger: header.Data.readUInt32LE(8), mpsHostname: hostname, raw: header.Data });
+            } else {
+                opt.unshift({ status: header.Status });
+            }
+            fn.apply(this, opt);
+        }, callback, optional);
+    }
+    this.getProtocolVersion = function getProtocolVersion(callback) {
+        var optional = [];
+        for (var i = 1; i < arguments.length; ++i) { opt.push(arguments[i]); }
+
+        if (!this._tmpSession) { this._tmpSession = heci.create(); this._tmpSession.parent = this; }
+        this._tmpSession.doIoctl(heci.IOCTL.HECI_VERSION, Buffer.alloc(5), Buffer.alloc(5), function (status, buffer, self, fn, opt) {
+            if (status == 0) {
+                var result = buffer.readUInt8(0).toString() + '.' + buffer.readUInt8(1).toString() + '.' + buffer.readUInt8(2).toString() + '.' + buffer.readUInt16BE(3).toString();
+                opt.unshift(result);
+                fn.apply(self, opt);
+            }
+            else {
+                opt.unshift(null);
+                fn.apply(self, opt);
+            }
+
+        }, this, callback, optional);
+    }
+    this.startConfigurationHBased = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
+        if ((certHash == null) || ((certHash.length != 32) && (certHash.length != 48))) { func({ status: -101 }); }
+        this.stopConfiguration(function (status) {
+            if (status == 0) {
+                // We stopped the configuration, wait 20 seconds before starting up again.
+                var f = function tf() { delete tf.parent.xtimeout; tf.parent.startConfigurationHBasedEx(certHash, hostVpn, dnsSuffixList, func); }
+                f.parent = this;
+                this.xtimeout = setTimeout(f, 20000);
+            } else {
+                // We are not in the connect mode, this is good, start configuration right away.
+                this.startConfigurationHBasedEx(certHash, hostVpn, dnsSuffixList, func);
+            }
+        })
+    }
+    this.startConfigurationHBasedEx = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) {
+        var optional = [];
+        for (var i = 4; i < arguments.length; ++i) { optional.push(arguments[i]); }
+
+        // Format the command
+        var data = Buffer.alloc(1 + 64 + 4 + 4 + ((dnsSuffixList != null) ? 320 : 0));
+        data[0] = (certHash.length == 48) ? 3 : 2 // Write certificate hash type: SHA256 = 2, SHA384 = 3
+        certHash.copy(data, 1); // Write the hash
+        data.writeUInt32LE(hostVpn ? 1 : 0, 65); // Write is HostVPN is enabled
+        if (dnsSuffixList != null) {
+            data.writeUInt32LE(dnsSuffixList.length, 69); // Write the number of DNS Suffix, from 0 to 4
+            var ptr = 73;
+            for (var i = 0; i < dnsSuffixList.length; i++) { ptr += data.write(dnsSuffixList[i], ptr) + 1; } // Write up to 4 DNS Suffix with null seperation.
+        }
+
+        // Send the command
+        this.sendCommand(139, data, function (header, fn, opt) {
+            if (header.Status == 0) {
+                var amtHash = null;
+                if (header.Data[0] == 2) { amtHash = header.Data.slice(1, 33); } // SHA256
+                if (header.Data[0] == 3) { amtHash = header.Data.slice(1, 49); } // SHA384
+                opt.unshift({ status: header.Status, hash: amtHash.toString('hex') });
+            } else {
+                opt.unshift({ status: header.Status });
+            }
+            fn.apply(this, opt);
+        }, func, optional);
+    }
+}
+
+module.exports = amt_heci;
+
+
+/*
+AMT_STATUS_SUCCESS = 0,
+AMT_STATUS_INTERNAL_ERROR = 1,
+AMT_STATUS_INVALID_AMT_MODE = 3,
+AMT_STATUS_INVALID_MESSAGE_LENGTH = 4,
+AMT_STATUS_MAX_LIMIT_REACHED = 23,
+AMT_STATUS_INVALID_PARAMETER = 36,
+AMT_STATUS_RNG_GENERATION_IN_PROGRESS = 47,
+AMT_STATUS_RNG_NOT_READY = 48,
+AMT_STATUS_CERTIFICATE_NOT_READY = 49,
+AMT_STATUS_INVALID_HANDLE = 2053
+AMT_STATUS_NOT_FOUND = 2068,
+*/

+ 1037 - 0
agents/modules_meshcore/computer-identifiers.js

@@ -0,0 +1,1037 @@
+/*
+Copyright 2019-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+function trimIdentifiers(val)
+{
+    for(var v in val)
+    {
+        if (!val[v] || val[v] == 'None' || val[v] == '' || val[v].trim() == '') { delete val[v]; }
+    }
+}
+function trimResults(val)
+{
+    var i, x;
+    for (i = 0; i < val.length; ++i)
+    {
+        for (x in val[i])
+        {
+            if (x.startsWith('_'))
+            {
+                delete val[i][x];
+            }
+            else
+            {
+                if (val[i][x] == null || val[i][x] == 0)
+                {
+                    delete val[i][x];
+                }
+            }
+        }
+    }
+}
+function brief(headers, obj)
+{
+    var i, x;
+    for (x = 0; x < obj.length; ++x)
+    {
+        for (i in obj[x])
+        {
+            if (!headers.includes(i))
+            {
+                delete obj[x][i];
+            }
+        }
+    }
+    return (obj);
+}
+
+function dataHandler(c)
+{
+    this.str += c.toString();
+}
+
+function linux_identifiers()
+{
+    var identifiers = {};
+    var ret = {};
+    var values = {};
+
+    if (!require('fs').existsSync('/sys/class/dmi/id')) {         
+        if (require('fs').existsSync('/sys/firmware/devicetree/base/model')) {
+            if (require('fs').readFileSync('/sys/firmware/devicetree/base/model').toString().trim().startsWith('Raspberry')) {
+                identifiers['board_vendor'] = 'Raspberry Pi';
+                identifiers['board_name'] = require('fs').readFileSync('/sys/firmware/devicetree/base/model').toString().trim();
+                identifiers['board_serial'] = require('fs').readFileSync('/sys/firmware/devicetree/base/serial-number').toString().trim();
+                const memorySlots = [];
+                var child = require('child_process').execFile('/bin/sh', ['sh']);
+                child.stdout.str = ''; child.stdout.on('data', dataHandler);
+                child.stdin.write('vcgencmd get_mem arm && vcgencmd get_mem gpu\nexit\n');
+                child.waitExit();
+                try { 
+                    const lines = child.stdout.str.trim().split('\n');
+                    if (lines.length == 2) {
+                        memorySlots.push({ Locator: "ARM Memory", Size: lines[0].split('=')[1].trim() })
+                        memorySlots.push({ Locator: "GPU Memory", Size: lines[1].split('=')[1].trim() })
+                        ret.memory = { Memory_Device: memorySlots };
+                    }
+                } catch (xx) { }
+            } else {
+                throw('Unknown board');
+            }
+        } else {
+            throw ('this platform does not have DMI statistics');
+        }
+    } else {
+        var entries = require('fs').readdirSync('/sys/class/dmi/id');
+        for (var i in entries) {
+            if (require('fs').statSync('/sys/class/dmi/id/' + entries[i]).isFile()) {
+                try {
+                    ret[entries[i]] = require('fs').readFileSync('/sys/class/dmi/id/' + entries[i]).toString().trim();
+                } catch(z) { }
+                if (ret[entries[i]] == 'None') { delete ret[entries[i]]; }
+            }
+        }
+        entries = null;
+
+        identifiers['bios_date'] = ret['bios_date'];
+        identifiers['bios_vendor'] = ret['bios_vendor'];
+        identifiers['bios_version'] = ret['bios_version'];
+        identifiers['bios_serial'] = ret['product_serial'];
+        identifiers['board_name'] = ret['board_name'];
+        identifiers['board_serial'] = ret['board_serial'];
+        identifiers['board_vendor'] = ret['board_vendor'];
+        identifiers['board_version'] = ret['board_version'];
+        identifiers['product_uuid'] = ret['product_uuid'];
+        identifiers['product_name'] = ret['product_name'];
+    }
+
+    try {
+        identifiers['bios_mode'] = (require('fs').statSync('/sys/firmware/efi').isDirectory() ? 'UEFI': 'Legacy');
+    } catch (ex) { identifiers['bios_mode'] = 'Legacy'; }
+
+    var child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', dataHandler);
+    child.stdin.write('cat /proc/cpuinfo | grep -i "model name" | ' + "tr '\\n' ':' | awk -F: '{ print $2 }'\nexit\n");
+    child.waitExit();
+    identifiers['cpu_name'] = child.stdout.str.trim();
+    if (identifiers['cpu_name'] == "") { // CPU BLANK, check lscpu instead
+        child = require('child_process').execFile('/bin/sh', ['sh']);
+        child.stdout.str = ''; child.stdout.on('data', dataHandler);
+        child.stdin.write('lscpu | grep -i "model name" | ' + "tr '\\n' ':' | awk -F: '{ print $2 }'\nexit\n");
+        child.waitExit();
+        identifiers['cpu_name'] = child.stdout.str.trim();
+    }
+    child = null;
+
+
+    // Fetch GPU info
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', dataHandler);
+    child.stdin.write("lspci | grep ' VGA ' | tr '\\n' '`' | awk '{ a=split($0,lines" + ',"`"); printf "["; for(i=1;i<a;++i) { split(lines[i],gpu,"r: "); printf "%s\\"%s\\"", (i==1?"":","),gpu[2]; } printf "]"; }\'\nexit\n');
+    child.waitExit();
+    try { identifiers['gpu_name'] = JSON.parse(child.stdout.str.trim()); } catch (xx) { }
+    child = null;
+
+    // Fetch Storage Info
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', dataHandler);
+    child.stdin.write("lshw -class disk -disable network | tr '\\n' '`' | awk '" + '{ len=split($0,lines,"*"); printf "["; for(i=2;i<=len;++i) { model=""; caption=""; size=""; clen=split(lines[i],item,"`"); for(j=2;j<clen;++j) { split(item[j],tokens,":"); split(tokens[1],key," "); if(key[1]=="description") { caption=substr(tokens[2],2); } if(key[1]=="product") { model=substr(tokens[2],2); } if(key[1]=="size") { size=substr(tokens[2],2);  } } if(model=="") { model=caption; } if(caption!="" || model!="") { printf "%s{\\"Caption\\":\\"%s\\",\\"Model\\":\\"%s\\",\\"Size\\":\\"%s\\"}",(i==2?"":","),caption,model,size; }  } printf "]"; }\'\nexit\n');
+    child.waitExit();
+    try { identifiers['storage_devices'] = JSON.parse(child.stdout.str.trim()); } catch (xx) { }
+    child = null;
+
+    // Fetch storage volumes using df
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', dataHandler);
+    child.stdin.write('df -T -x tmpfs -x devtmpfs -x efivarfs | awk \'NR==1 || $1 ~ ".+"{print $3, $4, $5, $7, $2}\' | awk \'NR>1 {printf "{\\"size\\":\\"%s\\",\\"used\\":\\"%s\\",\\"available\\":\\"%s\\",\\"mount_point\\":\\"%s\\",\\"type\\":\\"%s\\"},", $1, $2, $3, $4, $5}\' | sed \'$ s/,$//\' | awk \'BEGIN {printf "["} {printf "%s", $0} END {printf "]"}\'\nexit\n');
+    child.waitExit();
+    try { ret.volumes = JSON.parse(child.stdout.str.trim()); } catch (xx) { }
+    child = null;
+
+    values.identifiers = identifiers;
+    values.linux = ret;
+    trimIdentifiers(values.identifiers);
+
+    var dmidecode = require('lib-finder').findBinary('dmidecode');
+    if (dmidecode != null)
+    {
+        child = require('child_process').execFile('/bin/sh', ['sh']);
+        child.stdout.str = ''; child.stdout.on('data', dataHandler);
+        child.stderr.str = ''; child.stderr.on('data', dataHandler);
+        child.stdin.write(dmidecode + " -t memory | tr '\\n' '`' | ");
+        child.stdin.write(" awk '{ ");
+        child.stdin.write('   printf("[");');
+        child.stdin.write('   comma="";');
+        child.stdin.write('   c=split($0, lines, "``");');
+        child.stdin.write('   for(i=1;i<=c;++i)');
+        child.stdin.write('   {');
+        child.stdin.write('      d=split(lines[i], val, "`");');
+        child.stdin.write('      split(val[1], tokens, ",");');
+        child.stdin.write('      split(tokens[2], dmitype, " ");');
+        child.stdin.write('      dmi = dmitype[3]+0; ');
+        child.stdin.write('      if(dmi == 5 || dmi == 6 || dmi == 16 || dmi == 17)');
+        child.stdin.write('      {');
+        child.stdin.write('          ccx="";');
+        child.stdin.write('          printf("%s{\\"%s\\": {", comma, val[2]);');
+        child.stdin.write('          for(j=3;j<d;++j)');
+        child.stdin.write('          {');
+        child.stdin.write('             sub(/^[ \\t]*/,"",val[j]);');
+        child.stdin.write('             if(split(val[j],tmp,":")>1)');
+        child.stdin.write('             {');
+        child.stdin.write('                sub(/^[ \\t]*/,"",tmp[2]);');
+        child.stdin.write('                gsub(/ /,"",tmp[1]);');
+        child.stdin.write('                printf("%s\\"%s\\": \\"%s\\"", ccx, tmp[1], tmp[2]);');
+        child.stdin.write('                ccx=",";');
+        child.stdin.write('             }');
+        child.stdin.write('          }');
+        child.stdin.write('          printf("}}");');
+        child.stdin.write('          comma=",";');
+        child.stdin.write('      }');
+        child.stdin.write('   }');
+        child.stdin.write('   printf("]");');
+        child.stdin.write("}'\nexit\n");
+        child.waitExit();
+
+        try
+        {
+            var j = JSON.parse(child.stdout.str);
+            var i, key, key2;
+            for (i = 0; i < j.length; ++i)
+            {
+                for (key in j[i])
+                {
+                    delete j[i][key]['ArrayHandle'];
+                    delete j[i][key]['ErrorInformationHandle'];
+                    for (key2 in j[i][key])
+                    {
+                        if (j[i][key][key2] == 'Unknown' || j[i][key][key2] == 'Not Specified' || j[i][key][key2] == '')
+                        {
+                            delete j[i][key][key2];
+                        }
+                    }
+                }
+            }
+
+            if(j.length > 0){
+                var mem = {};
+                for (i = 0; i < j.length; ++i)
+                {
+                    for (key in j[i])
+                    {
+                        if (mem[key] == null) { mem[key] = []; }
+                        mem[key].push(j[i][key]);
+                    }
+                }
+                values.linux.memory = mem;
+            }
+        }
+        catch (e)
+        { }
+        child = null;
+    }
+
+    var usbdevices = require('lib-finder').findBinary('usb-devices');
+    if (usbdevices != null)
+    {
+        var child = require('child_process').execFile('/bin/sh', ['sh']);
+        child.stdout.str = ''; child.stdout.on('data', dataHandler);
+        child.stderr.str = ''; child.stderr.on('data', dataHandler);
+        child.stdin.write(usbdevices + " | tr '\\n' '`' | ");
+        child.stdin.write(" awk '");
+        child.stdin.write('{');
+        child.stdin.write('   comma="";');
+        child.stdin.write('   printf("[");');
+        child.stdin.write('   len=split($0, group, "``");');
+        child.stdin.write('   for(i=1;i<=len;++i)');
+        child.stdin.write('   {');
+        child.stdin.write('      comma2="";');
+        child.stdin.write('      xlen=split(group[i], line, "`");');
+        child.stdin.write('      scount=0;');
+        child.stdin.write('      for(x=1;x<xlen;++x)');
+        child.stdin.write('      {');
+        child.stdin.write('         if(line[x] ~ "^S:")');
+        child.stdin.write('         {');
+        child.stdin.write('            ++scount;');
+        child.stdin.write('         }');
+        child.stdin.write('      }');
+        child.stdin.write('      if(scount>0)');
+        child.stdin.write('      {');
+        child.stdin.write('         printf("%s{", comma); comma=",";');
+        child.stdin.write('         for(x=1;x<xlen;++x)');
+        child.stdin.write('         {');
+        child.stdin.write('            if(line[x] ~ "^T:")');
+        child.stdin.write('            {');
+        child.stdin.write('               comma3="";');
+        child.stdin.write('               printf("%s\\"hardware\\": {", comma2); comma2=",";');
+        child.stdin.write('               sub(/^T:[ \\t]*/, "", line[x]);');
+        child.stdin.write('               gsub(/= */, "=", line[x]);');
+        child.stdin.write('               blen=split(line[x], tokens, " ");');
+        child.stdin.write('               for(y=1;y<blen;++y)');
+        child.stdin.write('               {');
+        child.stdin.write('                  match(tokens[y],/=/);');
+        child.stdin.write('                  h=substr(tokens[y],1,RSTART-1);');
+        child.stdin.write('                  v=substr(tokens[y],RSTART+1);');
+        child.stdin.write('                  sub(/#/, "", h);');
+        child.stdin.write('                  printf("%s\\"%s\\": \\"%s\\"", comma3, h, v); comma3=",";');
+        child.stdin.write('               }');
+        child.stdin.write('               printf("}");');
+        child.stdin.write('            }');
+        child.stdin.write('            if(line[x] ~ "^S:")');
+        child.stdin.write('            {');
+        child.stdin.write('               sub(/^S:[ \\t]*/, "", line[x]);');
+        child.stdin.write('               match(line[x], /=/);');
+        child.stdin.write('               h=substr(line[x],1,RSTART-1);');
+        child.stdin.write('               v=substr(line[x],RSTART+1);');
+        child.stdin.write('               printf("%s\\"%s\\": \\"%s\\"", comma2, h,v); comma2=",";');
+        child.stdin.write('            }');
+        child.stdin.write('         }');
+        child.stdin.write('         printf("}");');
+        child.stdin.write('      }');
+        child.stdin.write('   }');
+        child.stdin.write('   printf("]");');
+        child.stdin.write("}'\nexit\n");
+        child.waitExit();
+
+        try
+        {
+            values.linux.usb = JSON.parse(child.stdout.str);
+        }
+        catch(x)
+        { }
+        child = null;
+    }
+
+    var pcidevices = require('lib-finder').findBinary('lspci');
+    if (pcidevices != null)
+    {
+        var child = require('child_process').execFile('/bin/sh', ['sh']);
+        child.stdout.str = ''; child.stdout.on('data', dataHandler);
+        child.stderr.str = ''; child.stderr.on('data', dataHandler);
+        child.stdin.write(pcidevices + " -m | tr '\\n' '`' | ");
+        child.stdin.write(" awk '");
+        child.stdin.write('{');
+        child.stdin.write('   printf("[");');
+        child.stdin.write('   comma="";');
+        child.stdin.write('   alen=split($0, lines, "`");');
+        child.stdin.write('   for(a=1;a<alen;++a)');
+        child.stdin.write('   {');
+        child.stdin.write('      match(lines[a], / /);');
+        child.stdin.write('      blen=split(lines[a], meta, "\\"");');
+        child.stdin.write('      bus=substr(lines[a], 1, RSTART);');
+        child.stdin.write('      gsub(/ /, "", bus);');
+        child.stdin.write('      printf("%s{\\"bus\\": \\"%s\\"", comma, bus); comma=",";');
+        child.stdin.write('      printf(", \\"device\\": \\"%s\\"", meta[2]);');
+        child.stdin.write('      printf(", \\"manufacturer\\": \\"%s\\"", meta[4]);');
+        child.stdin.write('      printf(", \\"description\\": \\"%s\\"", meta[6]);');
+        child.stdin.write('      if(meta[8] != "")');
+        child.stdin.write('      {');
+        child.stdin.write('         printf(", \\"subsystem\\": {");');
+        child.stdin.write('         printf("\\"manufacturer\\": \\"%s\\"", meta[8]);');
+        child.stdin.write('         printf(", \\"description\\": \\"%s\\"", meta[10]);');
+        child.stdin.write('         printf("}");');
+        child.stdin.write('      }');
+        child.stdin.write('      printf("}");');
+        child.stdin.write('   }');
+        child.stdin.write('   printf("]");');
+        child.stdin.write("}'\nexit\n");
+        child.waitExit();
+
+        try
+        {
+            values.linux.pci = JSON.parse(child.stdout.str);
+        }
+        catch (x)
+        { }
+        child = null;
+    }
+
+    // Linux Last Boot Up Time
+    try {
+        child = require('child_process').execFile('/usr/bin/uptime', ['', '-s']); // must include blank value at begining for some reason?
+        child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+        child.stderr.on('data', function () { });
+        child.waitExit();
+        var regex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
+        if (regex.test(child.stdout.str.trim())) {
+            values.linux.LastBootUpTime = child.stdout.str.trim();
+        } else {
+            child = require('child_process').execFile('/bin/sh', ['sh']);
+            child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+            child.stdin.write('date -d "@$(( $(date +%s) - $(awk \'{print int($1)}\' /proc/uptime) ))" "+%Y-%m-%d %H:%M:%S"\nexit\n');
+            child.waitExit();
+            if (regex.test(child.stdout.str.trim())) {
+                values.linux.LastBootUpTime = child.stdout.str.trim();
+            }
+        }
+        child = null;
+    } catch (ex) { }
+
+    // Linux TPM
+    try {
+        if (require('fs').statSync('/sys/class/tpm/tpm0').isDirectory()){
+            values.tpm = {
+                SpecVersion: require('fs').readFileSync('/sys/class/tpm/tpm0/tpm_version_major').toString().trim()
+            }
+        }
+    } catch (ex) { }
+
+    // Linux Batteries
+    try {
+        var batteries = require('fs').readdirSync('/sys/class/power_supply/');
+        if (batteries.length != 0) {
+            values.battery = [];
+            for (var i in batteries) {
+                const filesToRead = [
+                    'capacity', 'cycle_count', 'energy_full', 'energy_full_design',
+                    'energy_now', 'manufacturer', 'model_name', 'power_now',
+                    'serial_number', 'status', 'technology', 'voltage_now'
+                ];
+                const thedata = {};
+                for (var x in filesToRead) {
+                    try {   
+                        const content = require('fs').readFileSync('/sys/class/power_supply/' + batteries[i] + '/' + filesToRead[x]).toString().trim();
+                        thedata[filesToRead[x]] = /^\d+$/.test(content) ? parseInt(content, 10) : content;
+                    } catch (err) { }
+                }
+                if (Object.keys(thedata).length === 0) continue; // No data read, skip
+                const status = (thedata.status || '').toLowerCase();
+                const isCharging = status === 'charging';
+                const isDischarging = status === 'discharging';
+                const toMilli = function (val) { return Math.round((val || 0) / 1000) }; // Convert from µ units to m units (divide by 1000)
+                const batteryJson = {
+                    "InstanceName": batteries[i],
+                    "CycleCount": thedata.cycle_count || 0,
+                    "FullChargedCapacity": toMilli(thedata.energy_full),
+                    "Chemistry": (thedata.technology || ''),
+                    "DesignedCapacity": toMilli(thedata.energy_full_design),
+                    "DeviceName": thedata.model_name || "Battery",
+                    "ManufactureName": thedata.manufacturer || "Unknown",
+                    "SerialNumber": thedata.serial_number || "unknown",
+                    "ChargeRate": isCharging ? toMilli(thedata.power_now) : 0,
+                    "Charging": isCharging,
+                    "DischargeRate": isDischarging ? toMilli(thedata.power_now) : 0,
+                    "Discharging": isDischarging,
+                    "RemainingCapacity": toMilli(thedata.energy_now),
+                    "Voltage": toMilli(thedata.voltage_now),
+                    "Health": (thedata.energy_full && thedata.energy_full_design ? Math.floor((thedata.energy_full / thedata.energy_full_design) * 100) : 0),
+                    "BatteryCharge": (thedata.energy_now && thedata.energy_full ? Math.floor((thedata.energy_now / thedata.energy_full) * 100) : 0)
+                };
+                values.battery.push(batteryJson);
+            }
+            if (values.battery.length == 0) { delete values.battery; }
+        }
+    } catch (ex) { }
+
+    return (values);
+}
+
+function windows_wmic_results(str)
+{
+    var lines = str.trim().split('\r\n');
+    var keys = lines[0].split(',');
+    var i, key, keyval;
+    var tokens;
+    var result = [];
+
+    console.log('Lines: ' + lines.length, 'Keys: ' + keys.length);
+
+    for (i = 1; i < lines.length; ++i)
+    {
+        var obj = {};
+        console.log('i: ' + i);
+        tokens = lines[i].split(',');
+        for (key = 0; key < keys.length; ++key)
+        {
+            var tmp = Buffer.from(tokens[key], 'binary').toString();
+            console.log(tokens[key], tmp);
+            tokens[key] = tmp == null ? '' : tmp;
+            if (tokens[key].trim())
+            {
+                obj[keys[key].trim()] = tokens[key].trim();
+            }
+        }
+        delete obj.Node;
+        result.push(obj);
+    }
+    return (result);
+}
+
+function windows_identifiers()
+{
+    var ret = { windows: {} };
+    var items, item, i;
+
+    ret['identifiers'] = {};
+
+    var values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_Bios", ['ReleaseDate', 'Manufacturer', 'SMBIOSBIOSVersion', 'SerialNumber']);
+    if(values[0]){
+        ret['identifiers']['bios_date'] = values[0]['ReleaseDate'];
+        ret['identifiers']['bios_vendor'] = values[0]['Manufacturer'];
+        ret['identifiers']['bios_version'] = values[0]['SMBIOSBIOSVersion'];
+        ret['identifiers']['bios_serial'] = values[0]['SerialNumber'];
+    }
+    ret['identifiers']['bios_mode'] = 'Legacy';
+
+    values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_BaseBoard", ['Product', 'SerialNumber', 'Manufacturer', 'Version']);
+    if(values[0]){
+        ret['identifiers']['board_name'] = values[0]['Product'];
+        ret['identifiers']['board_serial'] = values[0]['SerialNumber'];
+        ret['identifiers']['board_vendor'] = values[0]['Manufacturer'];
+        ret['identifiers']['board_version'] = values[0]['Version'];
+    }
+
+    values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_ComputerSystemProduct", ['UUID', 'Name']);
+    if(values[0]){
+        ret['identifiers']['product_uuid'] = values[0]['UUID'];
+        ret['identifiers']['product_name'] = values[0]['Name'];
+    }
+
+    values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_SystemEnclosure", ['SerialNumber', 'SMBIOSAssetTag', 'Manufacturer']);
+    if(values[0]){
+        ret['identifiers']['chassis_serial'] = values[0]['SerialNumber'];
+        ret['identifiers']['chassis_assettag'] = values[0]['SMBIOSAssetTag'];
+        ret['identifiers']['chassis_manufacturer'] = values[0]['Manufacturer'];
+    }
+
+    trimIdentifiers(ret.identifiers);
+
+    values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_PhysicalMemory");
+    if(values[0]){
+        trimResults(values);
+        ret.windows.memory = values;
+    }
+
+    values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_OperatingSystem");
+    if(values[0]){
+        trimResults(values);
+        ret.windows.osinfo = values[0];
+    }
+
+    values = require('win-wmi-fixed').query('ROOT\\CIMV2', "SELECT * FROM Win32_DiskPartition");
+    if(values[0]){
+        trimResults(values);
+        ret.windows.partitions = values;
+        for (var i in values) {
+            if (values[i].Type=='GPT: System') {
+                ret['identifiers']['bios_mode'] = 'UEFI';
+            }
+        }
+    }
+
+    values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_Processor", ['Caption', 'DeviceID', 'Manufacturer', 'MaxClockSpeed', 'Name', 'SocketDesignation']);
+    if(values[0]){
+        ret.windows.cpu = values;
+    }
+    
+    values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_VideoController", ['Name', 'CurrentHorizontalResolution', 'CurrentVerticalResolution']);
+    if(values[0]){
+        ret.windows.gpu = values;
+    }
+
+    values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_DiskDrive", ['Caption', 'DeviceID', 'Model', 'Partitions', 'Size', 'Status']);
+    if(values[0]){
+        ret.windows.drives = values;
+    }
+    
+    // Insert GPU names
+    ret.identifiers.gpu_name = [];
+    for (var gpuinfo in ret.windows.gpu)
+    {
+        if (ret.windows.gpu[gpuinfo].Name) { ret.identifiers.gpu_name.push(ret.windows.gpu[gpuinfo].Name); }
+    }
+
+    // Insert Storage Devices
+    ret.identifiers.storage_devices = [];
+    for (var dv in ret.windows.drives)
+    {
+        ret.identifiers.storage_devices.push({ Caption: ret.windows.drives[dv].Caption, Model: ret.windows.drives[dv].Model, Size: ret.windows.drives[dv].Size });
+    }
+
+    try { ret.identifiers.cpu_name = ret.windows.cpu[0].Name; } catch (x) { }
+
+    // Windows TPM
+    IntToStr = function (v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); };
+    try {
+        values = require('win-wmi').query('ROOT\\CIMV2\\Security\\MicrosoftTpm', "SELECT * FROM Win32_Tpm", ['IsActivated_InitialValue','IsEnabled_InitialValue','IsOwned_InitialValue','ManufacturerId','ManufacturerVersion','SpecVersion']);
+        if(values[0]) {
+            ret.tpm = {
+                SpecVersion: values[0].SpecVersion.split(",")[0],
+                ManufacturerId: IntToStr(values[0].ManufacturerId).replace(/[^\x00-\x7F]/g, ""),
+                ManufacturerVersion: values[0].ManufacturerVersion,
+                IsActivated: values[0].IsActivated_InitialValue,
+                IsEnabled: values[0].IsEnabled_InitialValue,
+                IsOwned: values[0].IsOwned_InitialValue,
+            }
+        }
+    } catch (ex) { }
+
+    // Windows Batteries
+    IntToStrLE = function (v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); };
+    try {
+        function mergeJSONArrays() {
+            var resultMap = {};
+            var result = [];
+            // Loop through all arguments (arrays)
+            for (var i = 0; i < arguments.length; i++) {
+                var currentArray = arguments[i];
+                // Skip if not an array
+                if (!currentArray || currentArray.constructor !== Array) {
+                    continue;
+                }
+                // Process each object in the array
+                for (var j = 0; j < currentArray.length; j++) {
+                    var obj = currentArray[j];
+                    // Skip if not an object or missing InstanceName
+                    if (!obj || typeof obj !== 'object' || !obj.InstanceName) {
+                        continue;
+                    }
+                    var name = obj.InstanceName;
+                    // Create new entry if it doesn't exist
+                    if (!resultMap[name]) {
+                        resultMap[name] = { InstanceName: name };
+                        result.push(resultMap[name]);
+                    }
+                    // Copy all properties except InstanceName
+                    for (var key in obj) {
+                        if (obj.hasOwnProperty(key) && key !== 'InstanceName') {
+                            resultMap[name][key] = obj[key];
+                        }
+                    }
+                }
+            }
+            return result;
+        }
+        values = require('win-wmi').query('ROOT\\WMI', "SELECT * FROM BatteryCycleCount",['InstanceName','CycleCount']);
+        var values2 = require('win-wmi').query('ROOT\\WMI', "SELECT * FROM BatteryFullChargedCapacity",['InstanceName','FullChargedCapacity']);
+        var values3 = require('win-wmi').query('ROOT\\WMI', "SELECT * FROM BatteryRuntime",['InstanceName','EstimatedRuntime']);
+        var values4 = require('win-wmi').query('ROOT\\WMI', "SELECT * FROM BatteryStaticData",['InstanceName','Chemistry','DesignedCapacity','DeviceName','ManufactureDate','ManufactureName','SerialNumber']);
+        for (i = 0; i < values4.length; ++i) {
+            if (values4[i].Chemistry) { values4[i].Chemistry = IntToStrLE(parseInt(values4[i].Chemistry)); }
+            if (values4[i].ManufactureDate) { if (values4[i].ManufactureDate.indexOf('*****') != -1) delete values4[i].ManufactureDate; }
+        }
+        var values5 = require('win-wmi').query('ROOT\\WMI', "SELECT * FROM BatteryStatus",['InstanceName','ChargeRate','Charging','DischargeRate','Discharging','RemainingCapacity','Voltage']);
+        var values6 = [];
+        if (values2.length > 0 && values4.length > 0) {
+            for (i = 0; i < values2.length; ++i) {
+                for (var j = 0; j < values4.length; ++j) {
+                    if (values2[i].InstanceName == values4[j].InstanceName) {
+                        if ((values4[j].DesignedCapacity && values4[j].DesignedCapacity > 0) && (values2[i].FullChargedCapacity && values2[i].FullChargedCapacity > 0)) {
+                            values6[i] = { 
+                                Health: Math.floor((values2[i].FullChargedCapacity / values4[j].DesignedCapacity) * 100),
+                                InstanceName: values2[i].InstanceName
+                            };
+                            if (values6[i].Health > 100) { values6[i].Health = 100; }
+                        } else {
+                            values6[i] = { Health: 0, InstanceName: values2[i].InstanceName };
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        var values7 = [];
+        if (values2.length > 0 && values5.length > 0) {
+            for (i = 0; i < values2.length; ++i) {
+                for (var j = 0; j < values5.length; ++j) {
+                    if (values2[i].InstanceName == values5[j].InstanceName) {
+                        if ((values2[i].FullChargedCapacity && values2[i].FullChargedCapacity > 0) && (values5[j].RemainingCapacity && values5[j].RemainingCapacity > 0)) {
+                            values7[i] = { 
+                                BatteryCharge: Math.floor((values5[j].RemainingCapacity / values2[i].FullChargedCapacity) * 100),
+                                InstanceName: values2[i].InstanceName
+                            };
+                        } else {
+                            values7[i] = { BatteryCharge: 0, InstanceName: values2[i].InstanceName };
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        ret.battery = mergeJSONArrays(values, values2, values3, values4, values5, values6, values7);
+    } catch (ex) { }
+
+    return (ret);
+}
+function macos_identifiers()
+{
+    var ret = { identifiers: {}, darwin: {} };
+    var child;
+
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep board-id | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
+    child.waitExit();
+    ret.identifiers.board_name = child.stdout.str.trim();
+
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep IOPlatformSerialNumber | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
+    child.waitExit();
+    ret.identifiers.board_serial = child.stdout.str.trim();
+
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep manufacturer | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
+    child.waitExit();
+    ret.identifiers.board_vendor = child.stdout.str.trim();
+
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep version | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
+    child.waitExit();
+    ret.identifiers.board_version = child.stdout.str.trim();
+
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep IOPlatformUUID | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
+    child.waitExit();
+    ret.identifiers.product_uuid = child.stdout.str.trim();
+
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stdin.write('sysctl -n machdep.cpu.brand_string\nexit\n');
+    child.waitExit();
+    ret.identifiers.cpu_name = child.stdout.str.trim();
+
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stdin.write('system_profiler SPMemoryDataType\nexit\n');
+    child.waitExit();
+    var lines = child.stdout.str.trim().split('\n');
+    if(lines.length > 0) {
+        const memorySlots = [];
+        if(lines[2].trim().includes('Memory Slots:')) { // OLD MACS WITH SLOTS
+            var memorySlots1 = child.stdout.str.split(/\n{2,}/).slice(3);
+            memorySlots1.forEach(function(slot,index) {
+                var lines = slot.split('\n');
+                if(lines.length == 1){ // start here
+                    if(lines[0].trim()!=''){
+                        var slotObj = { DeviceLocator: lines[0].trim().replace(/:$/, '') }; // Initialize name as an empty string
+                        var nextline = memorySlots1[index+1].split('\n');
+                        nextline.forEach(function(line) {
+                            if (line.trim() !== '') {
+                                var parts = line.split(':');
+                                var key = parts[0].trim();
+                                var value = parts[1].trim();
+                                value = (key == 'Part Number' || key == 'Manufacturer') ? hexToAscii(parts[1].trim()) : parts[1].trim();
+                                slotObj[key.replace(' ','')] = value; // Store attribute in the slot object
+                            }
+                        });
+                        memorySlots.push(slotObj);
+                    }
+                }
+            });
+        } else { // NEW MACS WITHOUT SLOTS
+            memorySlots.push({ DeviceLocator: "Onboard Memory", Size: lines[2].split(":")[1].trim(), PartNumber: lines[3].split(":")[1].trim(), Manufacturer: lines[4].split(":")[1].trim() })
+        }
+        ret.darwin.memory = memorySlots;
+    }
+
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stdin.write('diskutil info -all\nexit\n');
+    child.waitExit();
+    var sections = child.stdout.str.split('**********\n');
+    if(sections.length > 0){
+        var devices = [];
+        for (var i = 0; i < sections.length; i++) {
+            var lines = sections[i].split('\n');
+            var deviceInfo = {};
+            var wholeYes = false;
+            var physicalYes = false;
+            var oldmac = false;
+            for (var j = 0; j < lines.length; j++) {
+                var keyValue = lines[j].split(':');
+                var key = keyValue[0].trim();
+                var value = keyValue[1] ? keyValue[1].trim() : '';
+                if (key === 'Virtual') oldmac = true;
+                if (key === 'Whole' && value === 'Yes') wholeYes = true;
+                if (key === 'Virtual' && value === 'No') physicalYes = true;
+                if(value && key === 'Device / Media Name'){
+                    deviceInfo['Caption'] = value;
+                }
+                if(value && key === 'Disk Size'){
+                    deviceInfo['Size'] = value.split(' ')[0] + ' ' + value.split(' ')[1];
+                }
+            }
+            if (wholeYes) {
+                if (oldmac) {
+                    if (physicalYes) devices.push(deviceInfo);
+                } else {
+                    devices.push(deviceInfo);
+                }
+            }
+        }
+        ret.identifiers.storage_devices = devices;
+    }
+
+    // Fetch storage volumes using df
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', dataHandler);
+    child.stdin.write('df -aHY | awk \'NR>1 {printf "{\\"size\\":\\"%s\\",\\"used\\":\\"%s\\",\\"available\\":\\"%s\\",\\"mount_point\\":\\"%s\\",\\"type\\":\\"%s\\"},", $3, $4, $5, $10, $2}\' | sed \'$ s/,$//\' | awk \'BEGIN {printf "["} {printf "%s", $0} END {printf "]"}\'\nexit\n');
+    child.waitExit();
+    try {
+        ret.darwin.volumes = JSON.parse(child.stdout.str.trim());
+        for (var index = 0; index < ret.darwin.volumes.length; index++) {
+            if (ret.darwin.volumes[index].type == 'auto_home'){
+                ret.darwin.volumes.splice(index,1);
+            }
+        }
+        if (ret.darwin.volumes.length == 0) { // not sonima OS so dont show type for now
+            child = require('child_process').execFile('/bin/sh', ['sh']);
+            child.stdout.str = ''; child.stdout.on('data', dataHandler);
+            child.stdin.write('df -aH | awk \'NR>1 {printf "{\\"size\\":\\"%s\\",\\"used\\":\\"%s\\",\\"available\\":\\"%s\\",\\"mount_point\\":\\"%s\\"},", $2, $3, $4, $9}\' | sed \'$ s/,$//\' | awk \'BEGIN {printf "["} {printf "%s", $0} END {printf "]"}\'\nexit\n');
+            child.waitExit();
+            try {
+                ret.darwin.volumes = JSON.parse(child.stdout.str.trim());
+                for (var index = 0; index < ret.darwin.volumes.length; index++) {
+                    if (ret.darwin.volumes[index].size == 'auto_home'){
+                        ret.darwin.volumes.splice(index,1);
+                    }
+                }
+            } catch (xx) { }
+        }
+    } catch (xx) { }
+    child = null;
+
+    // MacOS Last Boot Up Time
+    try {
+        child = require('child_process').execFile('/usr/sbin/sysctl', ['', 'kern.boottime']); // must include blank value at begining for some reason?
+        child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+        child.stderr.on('data', function () { });
+        child.waitExit();
+        const timestampMatch = /\{ sec = (\d+), usec = \d+ \}/.exec(child.stdout.str.trim());
+        if (!ret.darwin) {
+            ret.darwin = { LastBootUpTime: parseInt(timestampMatch[1]) };
+        } else {
+            ret.darwin.LastBootUpTime = parseInt(timestampMatch[1]);
+        }
+        child = null;
+    } catch (ex) { }
+
+    trimIdentifiers(ret.identifiers);
+
+    child = null;
+    return (ret);
+}
+
+function hexToAscii(hexString) {
+    if(!hexString.startsWith('0x')) return hexString.trim();
+    hexString = hexString.startsWith('0x') ? hexString.slice(2) : hexString;
+    var str = '';
+    for (var i = 0; i < hexString.length; i += 2) {
+        var hexPair = hexString.substr(i, 2);
+        str += String.fromCharCode(parseInt(hexPair, 16));
+    }
+    str = str.replace(/[\u007F-\uFFFF]/g, ''); // Remove characters from 0x0080 to 0xFFFF
+    return str.trim();
+}
+
+function win_chassisType()
+{
+    // use new win-wmi-fixed module to get arrays correctly for time being
+    try {
+        var tokens = require('win-wmi-fixed').query('ROOT\\CIMV2', 'SELECT ChassisTypes FROM Win32_SystemEnclosure', ['ChassisTypes']);
+        if (tokens[0]) {
+            return (parseInt(tokens[0]['ChassisTypes'][0]));
+        }
+    } catch (e) {
+        return (2); // unknown
+    }
+}
+
+function win_systemType()
+{
+    try {
+        var tokens = require('win-wmi').query('ROOT\\CIMV2', 'SELECT PCSystemType FROM Win32_ComputerSystem', ['PCSystemType']);
+        if (tokens[0]) {
+            return (parseInt(tokens[0]['PCSystemType']));
+        } else {
+            return (parseInt(1)); // default is desktop
+        }
+    } catch (ex) {
+        return (parseInt(1)); // default is desktop
+    }
+
+}
+
+function win_formFactor(chassistype)
+{
+    var ret = 'DESKTOP';
+    switch (chassistype)
+    {
+        case 11:    // Handheld
+        case 30:    // Tablet
+        case 31:    // Convertible
+        case 32:    // Detachable
+            ret = 'TABLET';
+            break;
+        case 9:     // Laptop
+        case 10:    // Notebook
+        case 14:    // Sub Notebook
+            ret = 'LAPTOP';
+            break;
+        default:
+            ret = win_systemType() == 2 ? 'MOBILE' : 'DESKTOP';
+            break;
+    }
+
+    return (ret);
+}
+
+switch(process.platform)
+{
+    case 'linux':
+        module.exports = { _ObjectID: 'identifiers', get: linux_identifiers };
+        break;
+    case 'win32':
+        module.exports = { _ObjectID: 'identifiers', get: windows_identifiers, chassisType: win_chassisType, formFactor: win_formFactor, systemType: win_systemType };
+        break;
+    case 'darwin':
+        module.exports = { _ObjectID: 'identifiers', get: macos_identifiers };
+        break;
+    default:
+        module.exports = { get: function () { throw ('Unsupported Platform'); } };
+        break;
+}
+module.exports.isDocker = function isDocker()
+{
+    if (process.platform != 'linux') { return (false); }
+
+    var child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stdin.write("cat /proc/self/cgroup | tr '\n' '`' | awk -F'`' '{ split($1, res, " + '"/"); if(res[2]=="docker"){print "1";} }\'\nexit\n');
+    child.waitExit();
+    return (child.stdout.str != '');
+};
+module.exports.isBatteryPowered = function isBatteryOperated()
+{
+    var ret = false;
+    switch(process.platform)
+    {
+        default:
+            break;
+        case 'linux':
+            var devices = require('fs').readdirSync('/sys/class/power_supply');
+            for (var i in devices)
+            {
+                if (require('fs').readFileSync('/sys/class/power_supply/' + devices[i] + '/type').toString().trim() == 'Battery')
+                {
+                    ret = true;
+                    break;
+                }
+            }
+            break;
+        case 'win32':
+            var GM = require('_GenericMarshal');
+            var stats = GM.CreateVariable(12);
+            var kernel32 = GM.CreateNativeProxy('Kernel32.dll');
+            kernel32.CreateMethod('GetSystemPowerStatus');
+            if (kernel32.GetSystemPowerStatus(stats).Val != 0)
+            {
+                if(stats.toBuffer()[1] != 128 && stats.toBuffer()[1] != 255)
+                {
+                    ret = true;
+                }
+                else
+                {
+                    // No Battery detected, so lets check if there is supposed to be one
+                    var formFactor = win_formFactor(win_chassisType());
+                    return (formFactor == 'LAPTOP' || formFactor == 'TABLET' || formFactor == 'MOBILE');
+                }
+            }
+            break;
+        case 'darwin':
+            var child = require('child_process').execFile('/bin/sh', ['sh']);
+            child.stdout.str = ''; child.stdout.on('data', function(c){ this.str += c.toString(); });
+            child.stderr.str = ''; child.stderr.on('data', function(c){ this.str += c.toString(); });
+            child.stdin.write("pmset -g batt | tr '\\n' '`' | awk -F'`' '{ if(NF>2) { print \"true\"; }}'\nexit\n");
+            child.waitExit();
+            if(child.stdout.str.trim() != '') { ret = true; }
+            break;
+    }
+    return (ret);
+};
+module.exports.isVM = function isVM()
+{
+    var ret = false;
+    var id = this.get();
+    if (id.linux && id.linux.sys_vendor)
+    {
+        switch (id.linux.sys_vendor)
+        {
+            case 'VMware, Inc.':
+            case 'QEMU':
+            case 'Xen':
+                ret = true;
+                break;
+            default:
+                break;
+        }
+    }
+    if (id.identifiers.bios_vendor)
+    {
+        switch(id.identifiers.bios_vendor)
+        {
+            case 'VMware, Inc.':
+            case 'Xen':
+            case 'SeaBIOS':
+            case 'EFI Development Kit II / OVMF':
+            case 'Proxmox distribution of EDK II':
+                ret = true;
+                break;
+            default:
+                break;
+        }
+    }
+    if (id.identifiers.board_vendor && id.identifiers.board_vendor == 'VMware, Inc.') { ret = true; }
+    if (id.identifiers.board_name)
+    {
+        switch (id.identifiers.board_name)
+        {
+            case 'VirtualBox':
+            case 'Virtual Machine':
+                ret = true;
+                break;
+            default:
+                break;
+        }
+    }
+
+    if (process.platform == 'win32' && !ret)
+    {
+        for(var i in id.identifiers.gpu_name)
+        {
+            if(id.identifiers.gpu_name[i].startsWith('VMware '))
+            {
+                ret = true;
+                break;
+            }
+        }
+    }
+
+
+    if (!ret) { ret = this.isDocker(); }
+    return (ret);
+};
+
+// bios_date = BIOS->ReleaseDate
+// bios_vendor = BIOS->Manufacturer
+// bios_version = BIOS->SMBIOSBIOSVersion
+// board_name = BASEBOARD->Product = ioreg/board-id
+// board_serial = BASEBOARD->SerialNumber = ioreg/serial-number | ioreg/IOPlatformSerialNumber
+// board_vendor = BASEBOARD->Manufacturer = ioreg/manufacturer
+// board_version = BASEBOARD->Version

+ 290 - 0
agents/modules_meshcore/coretranslations.json

@@ -0,0 +1,290 @@
+{
+  "en": {
+    "allow": "Allow",
+    "deny": "Deny",
+    "autoAllowForFive": "Auto accept all connections for next 5 minutes",
+    "terminalConsent": "{0} requesting remote terminal access. Grant access?",
+    "desktopConsent": "{0} requesting remote desktop access. Grant access?",
+    "fileConsent": "{0} requesting remote file Access. Grant access?",
+    "terminalNotify": "{0} started a remote terminal session.",
+    "desktopNotify": "{0} started a remote desktop session.",
+    "fileNotify": "{0} started a remote file session.",
+    "privacyBar": "Sharing desktop with: {0}"
+  },
+  "cs": {
+    "allow": "Dovolit",
+    "deny": "Odmítnout",
+    "autoAllowForFive": "Automaticky přijímat všechna připojení na dalších 5 minut",
+    "terminalConsent": "{0} žádá o vzdálený terminálový přístup. Přístup povolen?",
+    "desktopConsent": "{0} žádá o přístup ke vzdálené ploše. Přístup povolen?",
+    "fileConsent": "{0} požaduje vzdálený přístup k souboru. Přístup povolen?",
+    "terminalNotify": "{0} zahájil relaci vzdáleného terminálu.",
+    "desktopNotify": "{0} zahájil relaci vzdálené plochy.",
+    "fileNotify": "{0} zahájil relaci vzdáleného souboru.",
+    "privacyBar": "Sdílení plochy s: {0}"
+  },
+  "de": {
+    "allow": "Erlauben",
+    "deny": "Verweigern",
+    "autoAllowForFive": "Alle Verbindungen für die nächsten 5 Minuten erlauben",
+    "terminalConsent": "{0} erbittet Fern-Terminalzugriff. Zugang erlauben?",
+    "desktopConsent": "{0} erbittet Fern-Desktopzugriff. Zugang erlauben?",
+    "fileConsent": "{0} erbittet Fern-Dateizugriff. Zugang erlauben?",
+    "terminalNotify": "{0} hat eine Fern-Terminalzugriff-Sitzung gestartet.",
+    "desktopNotify": "{0} hat eine Fern-Desktopzugriff-Sitzung gestartet.",
+    "fileNotify": "{0} hat eine Fern-Dateizugriff-Sitzung gestartet.",
+    "privacyBar": "Teile desktop mit: {0}"
+  },
+  "es": {
+    "allow": "Permitir",
+    "deny": "Negar",
+    "autoAllowForFive": "Aceptar automáticamente todas las conexiones durante los próximos 5 minutos",
+    "terminalConsent": "{0} solicitando acceso a terminal remoto. ¿Autorizará el acceso?",
+    "desktopConsent": "{0} solicita acceso a escritorio remoto. ¿Autorizará el acceso?",
+    "fileConsent": "{0} solicita acceso remoto al archivo. ¿Autorizará el acceso?",
+    "terminalNotify": "{0} inició una sesión de terminal remota.",
+    "desktopNotify": "{0} inició una sesión de escritorio remoto.",
+    "fileNotify": "{0} inició una sesión de archivo remoto.",
+    "privacyBar": "Compartir escritorio con: {0}"
+  },
+  "fi": {
+    "allow": "Sallia",
+    "deny": "Kieltää",
+    "autoAllowForFive": "Hyväksy automaattisesti kaikki yhteydet seuraavan 5 minuutin ajan",
+    "terminalConsent": "{0} pyytää etäpäätteen käyttöoikeutta. Myönnetäänkö käyttöoikeus?",
+    "desktopConsent": "{0} pyytää etätyöpöytäkäyttöä. Myönnetäänkö käyttöoikeus?",
+    "fileConsent": "{0} pyytää etäkäyttöoikeutta tiedostoon. Myönnetäänkö käyttöoikeus?",
+    "terminalNotify": "{0} aloitti etäpääteistunnon.",
+    "desktopNotify": "{0} aloitti etätyöpöytäistunnon.",
+    "fileNotify": "{0} aloitti etätiedostoistunnon.",
+    "privacyBar": "Työpöytä jaetaan seuraavien kanssa: {0}"
+  },
+  "fr": {
+    "allow": "Permettre",
+    "deny": "Refuser",
+    "autoAllowForFive": "Accepter automatiquement les connexions pendant les 5 prochaines minutes",
+    "terminalConsent": "{0} demande(nt) d'utilisation du terminal à distance. Autoriser l'accès ?",
+    "desktopConsent": "{0} demande(nt) l'utilisation du bureau à distance. Autoriser l'accès ?",
+    "fileConsent": "{0} demande(nt) d'accès à un fichier à distance. Autoriser l'accès ?",
+    "terminalNotify": "{0} a démarré une session de terminal distant.",
+    "desktopNotify": "{0} a démarré une session de bureau à distance.",
+    "fileNotify": "{0} a démarré une session de fichiers à distance.",
+    "privacyBar": "Partage du bureau avec : {0}"
+  },
+  "hi": {
+    "allow": "अनुमति",
+    "deny": "मना",
+    "autoAllowForFive": "अगले 5 मिनट के लिए सभी कनेक्शन स्वतः स्वीकार करें",
+    "terminalConsent": "{0} दूरस्थ टर्मिनल पहुंच का अनुरोध कर रहा है। अनुदान पहुँच?",
+    "desktopConsent": "{0} दूरस्थ डेस्कटॉप पहुंच का अनुरोध कर रहा है। अनुदान पहुँच?",
+    "fileConsent": "{0} दूरस्थ फ़ाइल एक्सेस का अनुरोध करना। अनुदान पहुँच?",
+    "terminalNotify": "{0} ने दूरस्थ टर्मिनल सत्र प्रारंभ किया।",
+    "desktopNotify": "{0} ने दूरस्थ डेस्कटॉप सत्र प्रारंभ किया।",
+    "fileNotify": "{0} ने दूरस्थ फ़ाइल सत्र प्रारंभ किया।",
+    "privacyBar": "इसके साथ डेस्कटॉप साझा करना: {0}"
+  },
+  "it": {
+    "allow": "Permettere",
+    "deny": "Negare",
+    "autoAllowForFive": "Accetta automaticamente tutte le connessioni per i prossimi 5 minuti",
+    "terminalConsent": "{0} che richiede l'accesso al terminale remoto. Concedere l'accesso?",
+    "desktopConsent": "{0} che richiede l'accesso al desktop remoto. Concedere l'accesso?",
+    "fileConsent": "{0} che richiede l'accesso al file remoto. Concedere l'accesso?",
+    "terminalNotify": "{0} ha avviato una sessione di terminale remoto.",
+    "desktopNotify": "{0} ha avviato una sessione desktop remoto.",
+    "fileNotify": "{0} ha avviato una sessione di file remota.",
+    "privacyBar": "Condivisione del desktop con: {0}"
+  },
+  "ja": {
+    "allow": "許可する",
+    "deny": "拒否",
+    "autoAllowForFive": "次の5分間はすべての接続を自動受け入れます",
+    "terminalConsent": "{0}リモート端末アクセスを要求しています。アクセス許可?",
+    "desktopConsent": "{0}リモートデスクトップアクセスを要求しています。アクセス許可?",
+    "fileConsent": "{0}リモートファイルアクセスを要求しています。アクセス許可?",
+    "terminalNotify": "{0}がリモートターミナルセッションを開始しました。",
+    "desktopNotify": "{0}はリモートデスクトップセッションを開始しました。",
+    "fileNotify": "{0}がリモートファイルセッションを開始しました。",
+    "privacyBar": "デスクトップの共有:{0}"
+  },
+  "ko": {
+    "allow": "허용하다",
+    "deny": "거부",
+    "terminalNotify": "{0}이(가) 원격 터미널 세션을 시작했습니다.",
+    "desktopNotify": "{0}이(가) 원격 데스크톱 세션을 시작했습니다.",
+    "fileNotify": "{0}이(가) 원격 파일 세션을 시작했습니다.",
+    "privacyBar": "다음과 데스크톱 공유: {0}",
+    "autoAllowForFive": "다음 5분 동안 모든 연결 자동 수락",
+    "terminalConsent": "{0} 원격 터미널 액세스를 요청합니다. 액세스 권한을 부여하시겠습니까?",
+    "desktopConsent": "{0} 원격 데스크톱 액세스를 요청합니다. 액세스 권한을 부여하시겠습니까?",
+    "fileConsent": "{0}이(가) 원격 파일 액세스를 요청합니다. 액세스 권한을 부여하시겠습니까?"
+  },
+  "nl": {
+    "allow": "Toestaan",
+    "deny": "Weigeren",
+    "autoAllowForFive": "Alle verbindingen automatisch accepteren voor de komende 5 minuten",
+    "terminalConsent": "{0} verzoekt om toegang tot externe terminal.Toegang verlenen?",
+    "desktopConsent": "{0} verzoekt om toegang tot extern bureaublad.Toegang verlenen?",
+    "fileConsent": "{0} verzoekt om externe bestandstoegang.Toegang verlenen?",
+    "terminalNotify": "{0} heeft een externe terminalsessie gestart.",
+    "desktopNotify": "{0} heeft een extern bureaubladsessie gestart.",
+    "fileNotify": "{0} heeft een externe bestandssessie gestart.",
+    "privacyBar": "Bureaublad delen met: {0}"
+  },
+  "pt": {
+    "allow": "Permitir",
+    "deny": "Negar",
+    "autoAllowForFive": "Aceita automaticamente todas as conexões pelos próximos 5 minutos",
+    "terminalConsent": "{0} está a pedir acesso ao terminal remoto. Conceder acesso?",
+    "desktopConsent": "{0} está a pedir acesso à área de trabalho remota. Conceder acesso?",
+    "fileConsent": "{0} está a pedir acesso remoto aos ficheiros. Conceder acesso?",
+    "terminalNotify": "{0} iniciou uma sessão de terminal remoto.",
+    "desktopNotify": "{0} iniciou uma sessão de área de trabalho remota.",
+    "fileNotify": "{0} iniciou uma sessão de ficheiro remoto.",
+    "privacyBar": "Compartilhando área de trabalho com: {0}"
+  },
+  "ru": {
+    "allow": "Разрешить",
+    "deny": "Отказать",
+    "autoAllowForFive": "Автоматически принимать все соединения в течение 5 минут",
+    "terminalConsent": "{0} запрашивает удаленный доступ к терминалу. Разрешить доступ?",
+    "desktopConsent": "{0} запрашивает удаленный доступ к рабочему столу. Разрешить доступ?",
+    "fileConsent": "{0} запрашивает удаленный доступ к файлам. Разрешить доступ?",
+    "terminalNotify": "{0} начал сеанс удаленного терминала.",
+    "desktopNotify": "{0} начал сеанс удаленного рабочего стола.",
+    "fileNotify": "{0} начал удаленный файловый сеанс.",
+    "privacyBar": "Доступ к рабочему столу предоставлен: {0}"
+  },
+  "sv": {
+    "allow": "Tillåta",
+    "deny": "Förneka",
+    "autoAllowForFive": "Acceptera alla anslutningar automatiskt under de kommande 5 minuterna",
+    "terminalConsent": "{0} begär åtkomst till fjärrterminal. Ge tillgång?",
+    "desktopConsent": "{0} begär åtkomst till fjärrskrivbord. Ge tillgång?",
+    "fileConsent": "{0} begär fjärråtkomst till fil. Ge tillgång?",
+    "terminalNotify": "{0} startade en fjärrterminalsession.",
+    "desktopNotify": "{0} startade en fjärrskrivbordssession.",
+    "fileNotify": "{0} startade en fjärrfilsession.",
+    "privacyBar": "Dela skrivbord med: {0}"
+  },
+  "tr": {
+    "allow": "İzin ver",
+    "deny": "İnkar etmek",
+    "autoAllowForFive": "Sonraki 5 dakika boyunca tüm bağlantıları otomatik olarak kabul et",
+    "terminalConsent": "{0} uzak terminal erişimi istiyor. Erişim izni veriyor musunuz?",
+    "desktopConsent": "{0} uzak masaüstü erişimi istiyor. Erişim izni veriyor musunuz?",
+    "fileConsent": "{0} uzak dosya Erişimi istiyor. Erişim izni veriyor musunuz?",
+    "terminalNotify": "{0} bir uzak terminal oturumu başlattı.",
+    "desktopNotify": "{0} bir uzak masaüstü oturumu başlattı.",
+    "fileNotify": "{0} bir uzak dosya oturumu başlattı.",
+    "privacyBar": "Masaüstünü şu kişilerle paylaşma: {0}"
+  },
+  "zh-chs": {
+    "allow": "允许",
+    "deny": "否定",
+    "autoAllowForFive": "在接下来的 5 分钟内自动接受所有连接",
+    "terminalConsent": "{0} 请求远程终端访问。授予访问权限?",
+    "desktopConsent": "{0} 请求远程桌面访问。授予访问权限?",
+    "fileConsent": "{0} 请求远程文件访问。授予访问权限?",
+    "terminalNotify": "{0} 启动了远程终端会话。",
+    "desktopNotify": "{0} 启动了远程桌面会话。",
+    "fileNotify": "{0} 启动了远程文件会话。",
+    "privacyBar": "与:{0} 共享桌面"
+  },
+  "da": {
+    "allow": "Tillad",
+    "deny": "Afslå",
+    "autoAllowForFive": "Accepter automatisk alle forbindelser i de næste 5 minutter",
+    "terminalConsent": "{0} anmoder om ekstern terminaladgang. Give adgang?",
+    "desktopConsent": "{0} anmoder om fjernskrivebordsadgang. Give adgang?",
+    "fileConsent": "{0} anmoder om fjernadgang til fil. Give adgang?",
+    "terminalNotify": "{0} startede en fjernterminalsession.",
+    "desktopNotify": "{0} startede en fjernskrivebordssession.",
+    "fileNotify": "{0} startede en ekstern filsession.",
+    "privacyBar": "Deler skrivebordet med: {0}"
+  },
+  "pl": {
+    "allow": "Zezwól",
+    "deny": "Odrzucono",
+    "autoAllowForFive": "Automatycznie akceptuj wszystkie połączenia przez następne 5 minut",
+    "terminalConsent": "{0} prosi o zdalny dostęp do terminala. Przyznać dostęp?",
+    "desktopConsent": "{0} prosi o zdalny dostęp do pulpitu. Przyznać dostęp?",
+    "fileConsent": "{0} prosi o zdalny dostęp do plików. Przyznać dostęp?",
+    "terminalNotify": "{0} rozpoczął sesję dostępu do terminala.",
+    "desktopNotify": "{0} rozpoczął sesję dostępu do pulpitu.",
+    "fileNotify": "{0} rozpoczął sesję dostępu do plików.",
+    "privacyBar": "Współdzielenie pulpitu z: {0}"
+  },
+  "pt-br": {
+    "allow": "Permitir",
+    "deny": "Negar",
+    "autoAllowForFive": "Aceitar todas conexões pelos próximos 5 minutos",
+    "terminalConsent": "{0} está solicitando acesso ao terminal. Permitir?",
+    "desktopConsent": "{0} está solicitando acesso a área de trabalho remota. Permitir?",
+    "fileConsent": "{0} está solicitando acesso aos arquivos. Permitir?",
+    "terminalNotify": "{0} iniciou uma sessão de terminal.",
+    "desktopNotify": "{0} iniciou uma sessão de área de trabalho remota.",
+    "fileNotify": "{0} {0} iniciou uma sessão de arquivos.",
+    "privacyBar": "Compartilhando área de trabalho com: {0}"
+  },
+  "zh-cht": {
+    "allow": "允許",
+    "deny": "否定",
+    "autoAllowForFive": "在接下來的 5 分鐘內自動接受所有連接",
+    "terminalConsent": "{0} 請求遠程終端訪問。授予訪問權限?",
+    "desktopConsent": "{0} 請求遠程桌面訪問。授予訪問權限?",
+    "fileConsent": "{0} 請求遠程文件訪問。授予訪問權限?",
+    "terminalNotify": "{0} 啟動了遠程終端會話。",
+    "desktopNotify": "{0} 啟動了遠程桌面會話。",
+    "fileNotify": "{0} 啟動了遠程文件會話。",
+    "privacyBar": "與:{0} 共享桌面"
+  },
+  "bs": {
+    "allow": "Dopustiti",
+    "deny": "Deny",
+    "autoAllowForFive": "Automatski prihvati sve veze u narednih 5 minuta",
+    "terminalConsent": "{0} zahtijeva pristup udaljenom terminalu. Odobriti pristup?",
+    "desktopConsent": "{0} zahtijeva pristup udaljenoj radnoj površini. Odobriti pristup?",
+    "fileConsent": "{0} zahtijeva udaljeni pristup fajlu. Odobriti pristup?",
+    "terminalNotify": "{0} je započeo sesiju udaljenog terminala.",
+    "desktopNotify": "{0} je započeo sesiju udaljene radne površine.",
+    "fileNotify": "{0} je započeo sesiju udaljenog fajla.",
+    "privacyBar": "Dijeljenje radne površine sa: {0}"
+  },
+  "hu": {
+    "allow": "Engedélyezés",
+    "deny": "Elutasítás",
+    "autoAllowForFive": "Csatlakozások automatikus elfogadása a következő 5 percben",
+    "terminalConsent": "{0} távoli parancssor,terminál hozzáférést kér. Engedélyezi a hozzáférést?",
+    "desktopConsent": "{0} távoli asztali hozzáférést kér. Engedélyezi a hozzáférést?",
+    "fileConsent": "{0} távoli fájlhozzáférést kér. Engedélyezi a hozzáférést?",
+    "terminalNotify": "{0} távoli parancssor munkamenetet indított.",
+    "desktopNotify": "{0} távoli asztali munkamenetet indított.",
+    "fileNotify": "{0} távoli fájlmunkamenetet indított.",
+    "privacyBar": "Asztal megosztás aktív: {0} felhasználóval"
+  },
+  "ca": {
+    "allow": "Permetre",
+    "deny": "Negar",
+    "autoAllowForFive": "Accepta automàticament totes les connexions durant els propers 5 minuts",
+    "terminalConsent": "{0} sol·licitant accés al terminal remot. Accés garantit?",
+    "desktopConsent": "{0} sol·licitant accés a l'escriptori remot. Accés garantit?",
+    "fileConsent": "{0} sol·licitant accés remot al fitxer. Accés garantit?",
+    "terminalNotify": "{0} va iniciar una sessió de terminal remota.",
+    "desktopNotify": "{0} va iniciar una sessió d'escriptori remot.",
+    "fileNotify": "{0} va iniciar una sessió de fitxer remota.",
+    "privacyBar": "Compartint escriptori amb: {0}"
+  },
+  "uk": {
+    "allow": "Дозволити",
+    "deny": "Відмовити",
+    "autoAllowForFive": "Автоматично підтверджувати всі підключення впродовж наступних 5 хвилин",
+    "terminalConsent": "{0} запитує дозвіл на віддалене використання вашого терміналу (командного рядка). Надати доступ?",
+    "desktopConsent": "{0} запитує дозвіл на віддалений доступ до робочого столу. Надати доступ?",
+    "fileConsent": "{0} запитує дозвіл на віддалене управління файлами. Надати доступ?",
+    "terminalNotify": "{0} почав віддалено використовувати ваш термінал (командний рядок).",
+    "desktopNotify": "{0} розпочав сеанс віддаленого робочого столу.",
+    "fileNotify": "{0} почав віддалене управління вашими файлами.",
+    "privacyBar": "Ви ділитесь робочим столом з: {0}"
+  }
+}

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 37 - 0
agents/modules_meshcore/linux-dhcp.js


+ 335 - 0
agents/modules_meshcore/monitor-border.js

@@ -0,0 +1,335 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+var red = 0xFF;
+var yellow = 0xFFFF;
+var GXxor = 0x6; //	src XOR dst
+var GXclear = 0x0;
+var ExposureMask = (1 << 15);
+
+function windows_monitorborder()
+{
+    this._ObjectID = 'monitor-info';
+    var info = require('monitor-info');
+    var gm = require('_GenericMarshal');
+    var user32 = gm.CreateNativeProxy('user32.dll');
+    
+    info.monitors = [];
+    user32.CreateMethod('GetDC');
+    user32.CreateMethod('ReleaseDC');
+    user32.CreateMethod('FillRect');
+    user32.CreateMethod('InvalidateRect');
+
+    var gdi32 = gm.CreateNativeProxy('gdi32.dll');
+    gdi32.CreateMethod('CreateSolidBrush');
+
+    var redBrush = gdi32.CreateSolidBrush(red);
+    var yellowBrush = gdi32.CreateSolidBrush(yellow);
+
+    require('events').EventEmitter.call(this);
+    this.on('~', function () { this.Stop(); });
+
+    this.Stop = function Stop()
+    {
+        info.redInterval = null;
+
+        var drawRect = gm.CreateVariable(16);
+        var drawRectBuffer = drawRect.toBuffer();
+
+        for (var i in info.monitors)
+        {
+            // Top
+            drawRectBuffer.writeInt32LE(info.monitors[i].left, 0);
+            drawRectBuffer.writeInt32LE(info.monitors[i].top, 4);
+            drawRectBuffer.writeInt32LE(info.monitors[i].left + (info.monitors[i].right - info.monitors[i].left), 8);
+            drawRectBuffer.writeInt32LE(info.monitors[i].bottom - info.monitors[i].top, 12);
+            user32.InvalidateRect(0, drawRect, 0);
+        }
+    }
+
+    this.Start = function Start()
+    {
+        info.getInfo().then(function (mon)
+        {
+            var drawRect = gm.CreateVariable(16);
+
+            info.monitors = mon;
+            info.dc = user32.GetDC(0);
+            info.state = 0;
+
+            info.redInterval = setInterval(function ()
+            {
+                info.state = (info.state + 1) % 8;
+
+                var drawRectBuffer = drawRect.toBuffer();
+                for(var i in info.monitors)
+                {
+                    drawRectBuffer.writeInt32LE(info.monitors[i].left, 0);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].top, 4);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].left + (info.monitors[i].right - info.monitors[i].left)/2, 8);
+                    drawRectBuffer.writeInt32LE(5, 12);
+                    user32.FillRect(info.dc, drawRect, (info.state == 0 || info.state == 4) ? yellowBrush : redBrush);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].left + (info.monitors[i].right - info.monitors[i].left) / 2, 0);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].top, 4);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].right, 8);
+                    drawRectBuffer.writeInt32LE(5, 12);
+                    user32.FillRect(info.dc, drawRect, (info.state == 1 || info.state == 5) ? yellowBrush : redBrush);
+
+
+                    drawRectBuffer.writeInt32LE(info.monitors[i].right - 5, 0);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].top, 4);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].right, 8);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].top + (info.monitors[i].bottom - info.monitors[i].top)/2, 12);
+                    user32.FillRect(info.dc, drawRect, (info.state == 2 || info.state == 6) ? yellowBrush : redBrush);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].right - 5, 0);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].top + (info.monitors[i].bottom - info.monitors[i].top) / 2, 4);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].right, 8);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].bottom, 12);
+                    user32.FillRect(info.dc, drawRect, (info.state == 3 || info.state == 7) ? yellowBrush : redBrush);
+
+
+                    drawRectBuffer.writeInt32LE(info.monitors[i].left + (info.monitors[i].right - info.monitors[i].left) / 2, 0);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].bottom - 5, 4);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].right, 8);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].bottom, 12);
+                    user32.FillRect(info.dc, drawRect, (info.state == 4 || info.state == 0) ? yellowBrush : redBrush);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].left, 0);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].bottom - 5, 4);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].left + (info.monitors[i].right - info.monitors[i].left) / 2, 8);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].bottom, 12);
+                    user32.FillRect(info.dc, drawRect, (info.state == 5 || info.state == 1) ? yellowBrush : redBrush);
+
+
+                    drawRectBuffer.writeInt32LE(info.monitors[i].left, 0);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].top + (info.monitors[i].bottom - info.monitors[i].top) / 2, 4);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].left + 5, 8);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].bottom, 12);
+                    user32.FillRect(info.dc, drawRect, (info.state == 6 || info.state == 2) ? yellowBrush : redBrush);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].left, 0);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].top, 4);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].left + 5, 8);
+                    drawRectBuffer.writeInt32LE(info.monitors[i].top + (info.monitors[i].bottom - info.monitors[i].top) / 2, 12);
+                    user32.FillRect(info.dc, drawRect, (info.state == 7 || info.state == 3) ? yellowBrush : redBrush);
+                }
+            }, 450);
+        });
+    }
+}
+
+function linux_monitorborder()
+{
+    var self = this;
+    this.displays = [];
+    this._ObjectID = 'monitor-info';
+    this._info = require('monitor-info');
+    this._isUnity = this._info.isUnity();
+
+    console.log('isUnity = ' + this._isUnity);
+
+    require('events').EventEmitter.call(this);
+    this.on('~', function () { this.Stop(); });
+
+    this.Stop = function Stop()
+    {
+        this._timeout = null;
+        if(!this._isUnity)
+        {
+            for(var i=0; i < this.displays.length; ++i)
+            {
+                if(this.displays[i].GC1 && this.displays[i].rootWindow)
+                {
+                    self._info._X11.XSetFunction(self.displays[i].display, self.displays[i].GC1, GXclear);
+                    self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].rootWindow, self.displays[i].GC1, 0, 0, self.displays[i].right, 0);
+                    self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].rootWindow, self.displays[i].GC1, self.displays[i].right, 0, self.displays[i].right, self.displays[i].bottom);
+                    self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].rootWindow, self.displays[i].GC1, 0, self.displays[i].bottom, self.displays[i].right, self.displays[i].bottom);
+                    self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].rootWindow, self.displays[i].GC1, 0, 0, 0, self.displays[i].bottom);
+
+                    this._info._X11.XFlush(this.displays[i].display);
+                }
+            }
+        }
+    }
+    this.Start = function Start()
+    {
+        this._info.getInfo().then(function (mon)
+        {
+            self.displays = mon;
+            console.log(mon.length + ' displays');
+            for(var i = 0; i<mon.length; ++i)
+            {
+                console.log('Width: ' + mon[i].right + ', Height: ' + mon[i].bottom);
+                mon[i].rootWindow = self._info._X11.XRootWindow(mon[i].display, mon[i].screenId);
+
+                if (self._isUnity)
+                {
+                    // We are unity, so we have to fake the borders with borderless windows
+                    var white = self._info._X11.XWhitePixel(mon[i].display, mon[i].screenId).Val;
+
+                    // Top
+                    mon[i].window_top = self._info._X11.XCreateSimpleWindow(mon[i].display, mon[i].rootWindow, 0, 0, mon[i].right, 5, 0, white, white);
+                    mon[i].window_top.gc = self._info._X11.XCreateGC(mon[i].display, mon[i].window_top, 0, 0);
+                    self._info._X11.XSetLineAttributes(mon[i].display, mon[i].window_top.gc, 10, 0, 1, 1);
+                    self._info._X11.XSetSubwindowMode(mon[i].display, mon[i].window_top.gc, 1);
+                    self._info.unDecorateWindow(mon[i].display, mon[i].window_top);
+                    self._info.setWindowSizeHints(mon[i].display, mon[i].window_top, 0, 0, mon[i].right, 5);
+
+                    // Right
+                    mon[i].window_right = self._info._X11.XCreateSimpleWindow(mon[i].display, mon[i].rootWindow, mon[i].right - 5, 0, 5, mon[i].bottom, 0, white, white);
+                    mon[i].window_right.gc = self._info._X11.XCreateGC(mon[i].display, mon[i].window_right, 0, 0);
+                    self._info._X11.XSetLineAttributes(mon[i].display, mon[i].window_right.gc, 10, 0, 1, 1);
+                    self._info._X11.XSetSubwindowMode(mon[i].display, mon[i].window_right.gc, 1);
+                    self._info.unDecorateWindow(mon[i].display, mon[i].window_right);
+                    self._info.setWindowSizeHints(mon[i].display, mon[i].window_right, mon[i].right - 5, 0, 5, mon[i].bottom);
+
+                    // Left
+                    mon[i].window_left = self._info._X11.XCreateSimpleWindow(mon[i].display, mon[i].rootWindow, 0, 0, 5, mon[i].bottom, 0, white, white);
+                    mon[i].window_left.gc = self._info._X11.XCreateGC(mon[i].display, mon[i].window_left, 0, 0);
+                    self._info._X11.XSetLineAttributes(mon[i].display, mon[i].window_left.gc, 10, 0, 1, 1);
+                    self._info._X11.XSetSubwindowMode(mon[i].display, mon[i].window_left.gc, 1);
+                    self._info.unDecorateWindow(mon[i].display, mon[i].window_left);
+                    self._info.setWindowSizeHints(mon[i].display, mon[i].window_left, 0, 0, 5, mon[i].bottom);
+
+                    // Bottom
+                    mon[i].window_bottom = self._info._X11.XCreateSimpleWindow(mon[i].display, mon[i].rootWindow, 0, mon[i].bottom - 5, mon[i].right, 5, 0, white, white);
+                    mon[i].window_bottom.gc = self._info._X11.XCreateGC(mon[i].display, mon[i].window_bottom, 0, 0);
+                    self._info._X11.XSetLineAttributes(mon[i].display, mon[i].window_bottom.gc, 10, 0, 1, 1);
+                    self._info._X11.XSetSubwindowMode(mon[i].display, mon[i].window_bottom.gc, 1);
+                    self._info.unDecorateWindow(mon[i].display, mon[i].window_bottom);
+                    self._info.setWindowSizeHints(mon[i].display, mon[i].window_bottom, 0, mon[i].bottom - 5, mon[i].right, 5);
+
+                    self._info._X11.XMapWindow(mon[i].display, mon[i].window_top);
+                    self._info._X11.XMapWindow(mon[i].display, mon[i].window_right);
+                    self._info._X11.XMapWindow(mon[i].display, mon[i].window_left);
+                    self._info._X11.XMapWindow(mon[i].display, mon[i].window_bottom);
+
+                    self._info.setAlwaysOnTop(mon[i].display, mon[i].rootWindow, mon[i].window_top);
+                    self._info.hideWindowIcon(mon[i].display, mon[i].rootWindow, mon[i].window_top);
+                    self._info.setAlwaysOnTop(mon[i].display, mon[i].rootWindow, mon[i].window_right);
+                    self._info.hideWindowIcon(mon[i].display, mon[i].rootWindow, mon[i].window_right);
+                    self._info.setAlwaysOnTop(mon[i].display, mon[i].rootWindow, mon[i].window_left);
+                    self._info.hideWindowIcon(mon[i].display, mon[i].rootWindow, mon[i].window_left);
+                    self._info.setAlwaysOnTop(mon[i].display, mon[i].rootWindow, mon[i].window_bottom);
+                    self._info.hideWindowIcon(mon[i].display, mon[i].rootWindow, mon[i].window_bottom);
+
+                    self._info._X11.XFlush(mon[i].display);
+                    mon[i].borderState = 0;
+                }
+                else
+                {
+                    // If we aren't unity, then we can just draw
+                    mon[i].GC1 = self._info._X11.XCreateGC(mon[i].display, mon[i].rootWindow, 0, 0);
+                    mon[i].borderState = 0;
+
+                    self._info._X11.XSetForeground(mon[i].display, mon[i].GC1, self._info._X11.XWhitePixel(mon[i].display, mon[i].screenId).Val); // White
+                    self._info._X11.XSetLineAttributes(mon[i].display, mon[i].GC1, 10, 0, 1, 1);
+                    self._info._X11.XSetSubwindowMode(mon[i].display, mon[i].GC1, 1);
+                }
+            }
+            self._info._XEvent = self._info._gm.CreateVariable(192);
+            self._timeout = setTimeout(self._isUnity ? self.unity_drawBorder : self.timeoutHandler, 250);
+        });
+    }
+
+    this.timeoutHandler = function()
+    {
+        for (var i = 0; i < self.displays.length; ++i) {
+            self.displays[i].borderState = (self.displays[i].borderState + 1) % 8;
+
+            // Top
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].GC1, (self.displays[i].borderState == 0 || self.displays[i].borderState == 4) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].rootWindow, self.displays[i].GC1, 0, 0, self.displays[i].right / 2, 0);
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].GC1, (self.displays[i].borderState == 1 || self.displays[i].borderState == 5) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].rootWindow, self.displays[i].GC1, self.displays[i].right / 2, 0, self.displays[i].right, 0);
+
+            // Right
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].GC1, (self.displays[i].borderState == 2 || self.displays[i].borderState == 6) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].rootWindow, self.displays[i].GC1, self.displays[i].right, 0, self.displays[i].right, self.displays[i].bottom / 2);
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].GC1, (self.displays[i].borderState == 3 || self.displays[i].borderState == 7) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].rootWindow, self.displays[i].GC1, self.displays[i].right, self.displays[i].bottom / 2, self.displays[i].right, self.displays[i].bottom);
+
+            // Bottom
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].GC1, (self.displays[i].borderState == 5 || self.displays[i].borderState == 1) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].rootWindow, self.displays[i].GC1, 0, self.displays[i].bottom, self.displays[i].right / 2, self.displays[i].bottom);
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].GC1, (self.displays[i].borderState == 4 || self.displays[i].borderState == 0) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].rootWindow, self.displays[i].GC1, self.displays[i].right / 2, self.displays[i].bottom, self.displays[i].right, self.displays[i].bottom);
+
+            // Left
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].GC1, (self.displays[i].borderState == 7 || self.displays[i].borderState == 3) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].rootWindow, self.displays[i].GC1, 0, 0, 0, self.displays[i].bottom / 2);
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].GC1, (self.displays[i].borderState == 6 || self.displays[i].borderState == 2) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].rootWindow, self.displays[i].GC1, 0, self.displays[i].bottom / 2, 0, self.displays[i].bottom);
+
+
+            self._info._X11.XFlush(self.displays[i].display);
+        }
+        self._timeout = setTimeout(self._isUnity ? self.unity_drawBorder : self.timeoutHandler, 400);
+    }
+    this.unity_drawBorder = function unity_drawBorder()
+    {
+        for (var i = 0; i < self.displays.length; ++i)
+        {
+            self.displays[i].borderState = (self.displays[i].borderState + 1) % 8;
+
+            // Top
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].window_top.gc, (self.displays[i].borderState == 0 || self.displays[i].borderState == 4) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].window_top, self.displays[i].window_top.gc, 0, 0, self.displays[i].right / 2, 0);
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].window_top.gc, (self.displays[i].borderState == 1 || self.displays[i].borderState == 5) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].window_top, self.displays[i].window_top.gc, self.displays[i].right / 2, 0, self.displays[i].right, 0);
+            self._info._X11.XFlush(self.displays[i].display);
+
+            // Right
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].window_right.gc, (self.displays[i].borderState == 2 || self.displays[i].borderState == 6) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].window_right, self.displays[i].window_right.gc, 0, 0, 0, self.displays[i].bottom / 2);
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].window_right.gc, (self.displays[i].borderState == 3 || self.displays[i].borderState == 7) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].window_right, self.displays[i].window_right.gc, 0, self.displays[i].bottom / 2, 0, self.displays[i].bottom);
+            self._info._X11.XFlush(self.displays[i].display);
+
+            // Bottom
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].window_bottom.gc, (self.displays[i].borderState == 5 || self.displays[i].borderState == 1) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].window_bottom, self.displays[i].window_bottom.gc, 0, 0, self.displays[i].right / 2, 0);
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].window_bottom.gc, (self.displays[i].borderState == 4 || self.displays[i].borderState == 0) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].window_bottom, self.displays[i].window_bottom.gc, self.displays[i].right / 2, 0, self.displays[i].right, 0);
+            self._info._X11.XFlush(self.displays[i].display);
+
+            // Left
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].window_left.gc, (self.displays[i].borderState == 7 || self.displays[i].borderState == 3) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].window_left, self.displays[i].window_left.gc, 0, 0, 0, self.displays[i].bottom / 2);
+            self._info._X11.XSetForeground(self.displays[i].display, self.displays[i].window_left.gc, (self.displays[i].borderState == 6 || self.displays[i].borderState == 2) ? 0xffff00 : 0xff0000);
+            self._info._X11.XDrawLine(self.displays[i].display, self.displays[i].window_left, self.displays[i].window_left.gc, 0, self.displays[i].bottom / 2, 0, self.displays[i].bottom);
+            self._info._X11.XFlush(self.displays[i].display);
+        }
+        self._timeout = setTimeout(self._isUnity ? self.unity_drawBorder : self.timeoutHandler, 400);
+    }
+}
+
+switch(process.platform)
+{
+    case 'win32':
+        module.exports = new windows_monitorborder();
+        break;
+    case 'linux':
+        module.exports = new linux_monitorborder();
+        break;
+    default:
+        break;
+}
+
+
+
+
+
+
+

+ 359 - 0
agents/modules_meshcore/smbios.js

@@ -0,0 +1,359 @@
+/*
+Copyright 2018 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+try { Object.defineProperty(Array.prototype, "peek", { value: function () { return (this.length > 0 ? this[this.length - 1] : undefined); } }); } catch (e) { }
+try { Object.defineProperty(String.prototype, "replaceAll", { value: function replaceAll(oldVal, newVal) { return (this.split(oldVal).join(newVal)); } }); } catch (e) { }
+
+var RSMB = 1381190978;
+var memoryLocation = { 0x1: 'Other', 0x2: 'Unknown', 0x3: 'System Board', 0x4: 'ISA', 0x5: 'EISA', 0x6: 'PCI', 0x7: 'MCA', 0x8: 'PCMCIA', 0x9: 'Proprietary', 0xA: 'NuBus', 0xA0: 'PC-98/C20', 0xA1: 'PC-98/C24', 0xA2: 'PC-98/E', 0xA3: 'PC-98/LB' };
+var wakeReason = ['Reserved', 'Other', 'Unknown', 'APM Timer', 'Modem Ring', 'LAN', 'Power Switch', 'PCI', 'AC Power'];
+
+// Fill the left with zeros until the string is of a given length
+function zeroLeftPad(str, len)
+{
+    if ((len == null) && (typeof (len) != 'number')) { return null; }
+    if (str == null) str = ''; // If null, this is to generate zero leftpad string
+    var zlp = '';
+    for (var i = 0; i < len - str.length; i++) { zlp += '0'; }
+    return zlp + str;
+}
+
+function SMBiosTables()
+{
+    this._ObjectID = 'SMBiosTable';
+    if (process.platform == 'win32') {
+        this._marshal = require('_GenericMarshal');
+        this._native = this._marshal.CreateNativeProxy("Kernel32.dll");
+
+        this._native.CreateMethod('EnumSystemFirmwareTables');
+        this._native.CreateMethod('GetSystemFirmwareTable');
+    }
+    if (process.platform == 'linux') {
+        this._canonicalizeData = function _canonicalizeData(data) {
+            var lines = data.toString().split('Header and Data:\x0A');
+            var MemoryStream = require('MemoryStream');
+            var ms = new MemoryStream();
+
+            for (var i = 1; i < lines.length; ++i) {
+                var tokens = lines[i].split('Strings:\x0A');
+                var header = tokens[0].split('\x0A\x0A')[0].replaceAll('\x0A', '').trim().replaceAll(' ', '').replaceAll('\x09', '');
+                ms.write(Buffer.from(header, 'hex'));
+                if (tokens.length > 1) {
+                    var strings = tokens[1].split('\x0A\x0A')[0].split('\x0A');
+                    var stringsFinal = [];
+                    for (var strx in strings) {
+                        var tmp = strings[strx].trim().replaceAll(' ', '').replaceAll('\x09', '');
+                        if (tmp && tmp[0] !== '"' && /^[0-9a-fA-F]+$/.test(tmp)) { stringsFinal.push(tmp); }
+                    }
+                    ms.write(Buffer.from(stringsFinal.join(''), 'hex'));
+                    ms.write(Buffer.from('00', 'hex'));
+                }
+                else {
+                    ms.write(Buffer.from('0000', 'hex'));
+                }
+            }
+            var retVal = ms.buffer;
+            retVal.ms = ms;
+            return (retVal);
+        };
+    }
+    this._parse = function _parse(SMData) {
+        var ret = {};
+        var pbyte;
+        var i = 0
+        var SMData;
+        var structcount = 0;
+
+        while (SMData && i < SMData.length)
+        {
+            var SMtype = SMData[i];
+            var SMlength = SMData[i + 1];
+
+            if (!ret[SMtype]) { ret[SMtype] = []; }
+            ret[SMtype].push(SMData.slice(i + 4, i + SMlength));
+            if (process.platform == 'win32') { ret[SMtype].peek()._ext = pbyte; }
+            i += SMlength;
+
+            ret[SMtype].peek()._strings = [];
+
+            while (SMData[i] != 0 && i <= SMData.length)
+            {
+                var strstart = i;
+
+                // Start of String, find end of string
+                while (SMData[i++] != 0 && i <= SMData.length);
+                try
+                {
+                    ret[SMtype].peek()._strings.push(SMData.slice(strstart, i).toString().trim());
+                }
+                catch (ee)
+                {
+                }
+            }
+            i += (ret[SMtype].peek()._strings.length == 0) ? 2 : 1;
+            ++structcount;
+            //console.log('End of Table[' + SMtype + ']: ' + i);
+        }
+        //console.log('Struct Count = ' + structcount);
+        return (ret);
+    };
+    this.get = function get(callback) {
+        if (process.platform == 'win32') {
+            var size = this._native.GetSystemFirmwareTable(RSMB, 0, 0, 0).Val;
+            //console.log('Table Size: ' + size);
+
+            var PtrSize = this._marshal.CreatePointer()._size;
+            var buffer = this._marshal.CreateVariable(size);
+            var written = this._native.GetSystemFirmwareTable(RSMB, 0, buffer, size).Val;
+            //console.log('Written Size: ' + written);
+
+            var rawBuffer = buffer.toBuffer();
+            var length = buffer.Deref(4, 4).toBuffer().readUInt32LE(0);
+
+            pbyte = buffer.Deref(8, length);
+            SMData = pbyte.toBuffer();
+
+            if (callback) { callback.apply(this, [this._parse(SMData)]); return; } else { return (this._parse(SMData)); }
+        }
+        if (process.platform == 'linux') {
+            var MemoryStream = require('MemoryStream');
+            this.child = require('child_process').execFile('/usr/sbin/dmidecode', ['dmidecode', '-u']);
+            this.child.SMBiosTable = this;
+            this.child.ms = new MemoryStream();
+            this.child.ms.callback = callback;
+            this.child.ms.child = this.child;
+            this.child.stdout.on('data', function (buffer) { this.parent.ms.write(buffer); });
+            this.child.on('exit', function () { this.ms.end(); });
+            this.child.ms.on('end', function () {
+                //console.log('read ' + this.buffer.length + ' bytes');
+                if (this.buffer.length < 300) {
+                    //console.log('Not enough permission to read SMBiosTable');
+                    if (this.callback) { this.callback.apply(this.child.SMBiosTable, []); }
+                }
+                else {
+                    var SMData = this.child.SMBiosTable._canonicalizeData(this.buffer);
+                    var j = this.child.SMBiosTable._parse(SMData);
+                    if (this.callback) { this.callback.apply(this.child.SMBiosTable, [j]); }
+                }
+            });
+            return;
+        }
+        if (callback) { callback.apply(this, [null]); return; } else { return (null); }
+    };
+    this.parse = function parse(data) {
+        var r = {};
+        try
+        {
+            r.processorInfo = this.processorInfo(data);
+        }
+        catch(e)
+        {
+        }
+        try
+        {
+            r.memoryInfo = this.memoryInfo(data);
+        }
+        catch(e)
+        {
+        }
+        try
+        {
+            r.systemInfo = this.systemInfo(data);
+        }
+        catch(e)
+        {
+        }
+        try
+        {
+            r.systemSlots = this.systemSlots(data);
+        }
+        catch(e)
+        {
+        }
+        try
+        {
+            r.amtInfo = this.amtInfo(data);
+        }
+        catch(e)
+        {
+        }
+        try
+        {
+            if (JSON.stringify(r).length > 65535) { r = {}; }
+        }
+        catch(ee)
+        {}
+        return r;
+    }
+    this.processorInfo = function processorInfo(data) {
+        if (!data) { throw ('no data'); }
+        var ret = [];
+        var ptype = ['ERROR', 'Other', 'Unknown', 'CPU', 'ALU', 'DSP', 'GPU'];
+        var statusString = ['Unknown', 'Enabled', 'Disabled by user', 'Disabled by BIOS', 'Idle', 'Reserved', 'Reserved', 'Other'];
+        var cpuid = 0;
+        while (data[4] && data[4].length > 0) {
+            var p = data[4].pop();
+            var populated = p[20] & 0x40;
+            var status = p[20] & 0x07
+            if (populated) {
+                var j = { _ObjectID: 'SMBiosTables.processorInfo' };
+                j.Processor = ptype[p[1]];
+                j.MaxSpeed = p.readUInt16LE(16) + ' Mhz';
+                if (p[31]) { j.Cores = p[31]; }
+                if (p[33]) { j.Threads = p[33]; }
+                j.Populated = 1;
+                j.Status = statusString[status];
+                j.Socket = p._strings[p[0] - 1];
+                j.Manufacturer = p._strings[p[3] - 1];
+                j.Version = p._strings[p[12] - 1];
+                ret.push(j);
+            }
+        }
+        return (ret);
+    };
+    this.memoryInfo = function memoryInfo(data) {
+        if (!data) { throw ('no data'); }
+        var retVal = { _ObjectID: 'SMBiosTables.memoryInfo' };
+        if (data[16]) {
+            var m = data[16].peek();
+            retVal.location = memoryLocation[m[0]];
+            if ((retVal.maxCapacityKb = m.readUInt32LE(3)) == 0x80000000) {
+                retVal.maxCapacityKb = 'A really big number';
+            }
+        }
+        return (retVal);
+    };
+    this.systemInfo = function systemInfo(data)
+    {
+        if (!data) { throw ('no data'); }
+        var retVal = { _ObjectID: 'SMBiosTables.systemInfo' };
+        if (data[1])
+        {
+            var si = data[1].peek();
+            var uuid = si.slice(4, 20);
+
+            retVal.uuid = [zeroLeftPad(uuid.readUInt32LE(0).toString(16), 8),
+            zeroLeftPad(uuid.readUInt16LE(4).toString(16), 4),
+            zeroLeftPad(uuid.readUInt16LE(6).toString(16), 4),
+            zeroLeftPad(uuid.readUInt16BE(8).toString(16), 4),
+            zeroLeftPad(uuid.slice(10).toString('hex').toLowerCase(), 12)].join('-');
+
+            retVal.wakeReason = wakeReason[si[20]];
+        }
+        return (retVal);
+    };
+    this.systemSlots = function systemSlots(data) {
+        if (!data) { throw ('no data'); }
+        var retVal = [];
+        if (data[9]) {
+            while (data[9].length > 0) {
+                var ss = data[9].pop();
+                retVal.push({ name: ss._strings[ss[0] - 1] });
+            }
+        }
+        return (retVal);
+    };
+    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')
+        {
+            var amt = data[130].peek();
+            retVal.AMT = amt[4] ? true : false;
+            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')
+                {
+                    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; }
+                    retVal.MEBX = settings.readUInt16LE(4).toString() + '.' + settings.readUInt16LE(6).toString() + '.' + settings.readUInt16LE(8).toString() + '.' + settings.readUInt16LE(10).toString();
+
+                    var mecap = settings.slice(20, 32);
+                    retVal.ManagementEngine = mecap.readUInt16LE(6).toString() + '.' + mecap.readUInt16LE(4).toString() + '.' + mecap.readUInt16LE(10).toString() + '.' + mecap.readUInt16LE(8).toString();
+
+                    //var lan = settings.slice(36, 48);
+                    //console.log(lan.toString('hex'));
+                    //retVal.LAN = (lan.readUInt16LE(10) & 0x03).toString() + '/' + ((lan.readUInt16LE(10) & 0xF8) >> 3).toString();
+
+                    //console.log(lan.readUInt16LE(3));
+                    //retVal.WLAN = (lan.readUInt16LE(3) & 0x07).toString() + '/' + ((lan.readUInt16LE(3) & 0xF8) >> 3).toString() + '/' + (lan.readUInt16LE(3) >> 8).toString();
+                }
+            }
+        }
+        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 = {
+        0: 'BIOS information',
+        1: 'System information',
+        2: 'Baseboard (or Module) information',
+        4: 'Processor information',
+        5: 'memory controller information',
+        6: 'Memory module information',
+        7: 'Cache information',
+        8: 'Port connector information',
+        9: 'System slots',
+        10: 'On board devices information',
+        11: 'OEM strings',
+        12: 'System configuration options',
+        13: 'BIOS language information',
+        14: 'Group associations',
+        15: 'System event log',
+        16: 'Physical memory array',
+        17: 'Memory device',
+        18: '32bit memory error information',
+        19: 'Memory array mapped address',
+        20: 'Memory device mapped address',
+        21: 'Built-in pointing device',
+        22: 'Portable battery',
+        23: 'System reset',
+        24: 'Hardware security',
+        25: 'System power controls',
+        26: 'Voltage probe',
+        27: 'Cooling device',
+        28: 'Temperature probe',
+        29: 'Electrical current probe',
+        30: 'Out-of-band remote access',
+        31: 'Boot integrity services (BIS) entry point',
+        32: 'System boot information',
+        33: '64bit memory error information',
+        34: 'Management device',
+        35: 'Management device component',
+        36: 'Management device threshold data',
+        37: 'Memory channel',
+        38: 'IPMI device information',
+        39: 'System power supply',
+        40: 'Additional information',
+        41: 'Onboard devices extended information',
+        42: 'Management controller host interface',
+        126: 'Inactive',
+        127: 'End-of-table'
+    }
+}
+
+module.exports = new SMBiosTables();

+ 325 - 0
agents/modules_meshcore/sysinfo.js

@@ -0,0 +1,325 @@
+/*
+Copyright 2019-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+var PDH_FMT_LONG = 0x00000100;
+var PDH_FMT_DOUBLE = 0x00000200;
+
+var promise = require('promise');
+if (process.platform == 'win32')
+{
+    var GM = require('_GenericMarshal');
+    GM.kernel32 = GM.CreateNativeProxy('kernel32.dll');
+    GM.kernel32.CreateMethod('GlobalMemoryStatusEx');
+
+    GM.pdh = GM.CreateNativeProxy('pdh.dll');
+    GM.pdh.CreateMethod('PdhAddEnglishCounterA');
+    GM.pdh.CreateMethod('PdhCloseQuery');
+    GM.pdh.CreateMethod('PdhCollectQueryData');
+    GM.pdh.CreateMethod('PdhGetFormattedCounterValue');
+    GM.pdh.CreateMethod('PdhGetFormattedCounterArrayA');
+    GM.pdh.CreateMethod('PdhOpenQueryA');
+    GM.pdh.CreateMethod('PdhRemoveCounter');
+}
+
+function windows_cpuUtilization()
+{
+    var p = new promise(function (res, rej) { this._res = res; this._rej = rej; });
+    p.counter = GM.CreateVariable(16);
+    p.cpu = GM.CreatePointer();
+    p.cpuTotal = GM.CreatePointer();
+    var err = 0;
+    if ((err = GM.pdh.PdhOpenQueryA(0, 0, p.cpu).Val) != 0) { p._rej(err); return; }
+
+    // This gets the CPU Utilization for each proc
+    if ((err = GM.pdh.PdhAddEnglishCounterA(p.cpu.Deref(), GM.CreateVariable('\\Processor(*)\\% Processor Time'), 0, p.cpuTotal).Val) != 0) { p._rej(err); return; }
+
+    if ((err = GM.pdh.PdhCollectQueryData(p.cpu.Deref()).Val != 0)) { p._rej(err); return; }
+    p._timeout = setTimeout(function (po)
+    {
+        var u = { cpus: [] };
+        var bufSize = GM.CreateVariable(4);
+        var itemCount = GM.CreateVariable(4);
+        var buffer, szName, item;
+        var e;
+        if ((e = GM.pdh.PdhCollectQueryData(po.cpu.Deref()).Val != 0)) { po._rej(e); return; }
+
+        if ((e = GM.pdh.PdhGetFormattedCounterArrayA(po.cpuTotal.Deref(), PDH_FMT_DOUBLE, bufSize, itemCount, 0).Val) == -2147481646)
+        {
+            buffer = GM.CreateVariable(bufSize.toBuffer().readUInt32LE());
+        }
+        else
+        {
+            po._rej(e);
+            return;
+        }
+        if ((e = GM.pdh.PdhGetFormattedCounterArrayA(po.cpuTotal.Deref(), PDH_FMT_DOUBLE, bufSize, itemCount, buffer).Val) != 0) { po._rej(e); return; }
+        for(var i=0;i<itemCount.toBuffer().readUInt32LE();++i)
+        {
+            item = buffer.Deref(i * 24, 24);
+            szName = item.Deref(0, GM.PointerSize).Deref();
+            if (szName.String == '_Total')
+            {
+                u.total = item.Deref(16, 8).toBuffer().readDoubleLE();
+            }
+            else
+            {
+                u.cpus[parseInt(szName.String)] = item.Deref(16, 8).toBuffer().readDoubleLE();
+            }
+        }
+
+        GM.pdh.PdhRemoveCounter(po.cpuTotal.Deref());
+        GM.pdh.PdhCloseQuery(po.cpu.Deref());
+        p._res(u);
+    }, 100, p);
+
+    return (p);
+}
+function windows_memUtilization()
+{
+    var info = GM.CreateVariable(64);
+    info.Deref(0, 4).toBuffer().writeUInt32LE(64);
+    GM.kernel32.GlobalMemoryStatusEx(info);
+
+    var ret =
+        {
+            MemTotal: require('bignum').fromBuffer(info.Deref(8, 8).toBuffer(), { endian: 'little' }),
+            MemFree: require('bignum').fromBuffer(info.Deref(16, 8).toBuffer(), { endian: 'little' })
+        };
+
+    ret.percentFree = ((ret.MemFree.div(require('bignum')('1048576')).toNumber() / ret.MemTotal.div(require('bignum')('1048576')).toNumber()) * 100);//.toFixed(2);
+    ret.percentConsumed = ((ret.MemTotal.sub(ret.MemFree).div(require('bignum')('1048576')).toNumber() / ret.MemTotal.div(require('bignum')('1048576')).toNumber()) * 100);//.toFixed(2);
+    ret.MemTotal = ret.MemTotal.toString();
+    ret.MemFree = ret.MemFree.toString();
+    return (ret);
+}
+
+var cpuLastIdle = [];
+var cpuLastSum = [];
+function linux_cpuUtilization() {
+    var ret = { cpus: [] };
+    var info = require('fs').readFileSync('/proc/stat');
+    var lines = info.toString().split('\n');
+    var columns;
+    var x, y;
+    var cpuNo = 0;
+    var currSum, currIdle, utilization;
+    for (var i in lines) {
+        columns = lines[i].split(' ');
+        if (!columns[0].startsWith('cpu')) { break; }
+
+        x = 0, currSum = 0;
+        while (columns[++x] == '');
+        for (y = x; y < columns.length; ++y) { currSum += parseInt(columns[y]); }
+        currIdle = parseInt(columns[3 + x]);
+
+        var diffIdle = currIdle - cpuLastIdle[cpuNo];
+        var diffSum = currSum - cpuLastSum[cpuNo];
+
+        utilization = (100 - ((diffIdle / diffSum) * 100));
+
+        cpuLastSum[cpuNo] = currSum;
+        cpuLastIdle[cpuNo] = currIdle;
+
+        if (!ret.total) {
+            ret.total = utilization;
+        } else {
+            ret.cpus.push(utilization);
+        }
+        ++cpuNo;
+    }
+
+    var p = new promise(function (res, rej) { this._res = res; this._rej = rej; });
+    p._res(ret);
+    return (p);
+}
+function linux_memUtilization()
+{
+    var ret = {};
+
+    var info = require('fs').readFileSync('/proc/meminfo').toString().split('\n');
+    var tokens;
+    for(var i in info)
+    {
+        tokens = info[i].split(' ');
+        switch(tokens[0])
+        {
+            case 'MemTotal:':
+                ret.total = parseInt(tokens[tokens.length - 2]);
+                break;
+            case 'MemAvailable:':
+                ret.free = parseInt(tokens[tokens.length - 2]);
+                break;
+        }
+    }
+    ret.percentFree = ((ret.free / ret.total) * 100);//.toFixed(2);
+    ret.percentConsumed = (((ret.total - ret.free) / ret.total) * 100);//.toFixed(2);
+    return (ret);
+}
+
+function macos_cpuUtilization()
+{
+    var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
+    var child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = '';
+    child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
+    child.stdin.write('top -l 1 | grep -E "^CPU"\nexit\n');
+    child.waitExit();
+
+    var lines = child.stdout.str.split('\n');
+    if (lines[0].length > 0)
+    {
+        var usage = lines[0].split(':')[1];
+        var bdown = usage.split(',');
+
+        var tot = parseFloat(bdown[0].split('%')[0].trim()) + parseFloat(bdown[1].split('%')[0].trim());
+        ret._res({total: tot, cpus: []});
+    }
+    else
+    {
+        ret._rej('parse error');
+    }
+
+    return (ret);
+}
+function macos_memUtilization()
+{
+    var mem = { };
+    var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
+    var child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = '';
+    child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
+    child.stdin.write('top -l 1 | grep -E "^Phys"\nexit\n');
+    child.waitExit();
+
+    var lines = child.stdout.str.split('\n');
+    if (lines[0].length > 0)
+    {
+        var usage = lines[0].split(':')[1];
+        var bdown = usage.split(',');
+        if (bdown.length > 2){ // new style - PhysMem: 5750M used (1130M wired, 634M compressor), 1918M unused.
+            mem.MemFree = parseInt(bdown[2].trim().split(' ')[0]);
+        } else { // old style - PhysMem: 6683M used (1606M wired), 9699M unused.
+            mem.MemFree = parseInt(bdown[1].trim().split(' ')[0]);
+        }
+        mem.MemUsed = parseInt(bdown[0].trim().split(' ')[0]);
+        mem.MemTotal = (mem.MemFree + mem.MemUsed);
+        mem.percentFree = ((mem.MemFree / mem.MemTotal) * 100);//.toFixed(2);
+        mem.percentConsumed = (((mem.MemTotal - mem.MemFree) / mem.MemTotal) * 100);//.toFixed(2);
+        return (mem);
+    }
+    else
+    {
+        throw ('Parse Error');
+    }
+}
+
+function windows_thermals()
+{
+    var ret = [];
+    try {
+        ret = require('win-wmi').query('ROOT\\WMI', 'SELECT CurrentTemperature,InstanceName FROM MSAcpi_ThermalZoneTemperature',['CurrentTemperature','InstanceName']);
+        if (ret[0]) {
+            for (var i = 0; i < ret.length; ++i) {
+                ret[i]['CurrentTemperature'] = ((parseFloat(ret[i]['CurrentTemperature']) / 10) - 273.15).toFixed(2);
+            }
+        }
+    } catch (ex) { }
+    return (ret);
+}
+
+function linux_thermals()
+{
+    var ret = [];
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); });
+    child.stdin.write("for folder in /sys/class/thermal/thermal_zone*/; do [ -e \"$folder/temp\" ] && echo \"$(cat \"$folder/temp\"),$(cat \"$folder/type\")\"; done\nexit\n");
+    child.waitExit();
+    if(child.stdout.str.trim()!='')
+    {
+        var lines = child.stdout.str.trim().split('\n');
+        for (var i = 0; i < lines.length; ++i)
+        {
+            var line = lines[i].trim().split(',');
+            ret.push({CurrentTemperature: (parseFloat(line[0])/1000), InstanceName: line[1]});
+        }
+    }
+    child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); });
+    child.stdin.write("for mon in /sys/class/hwmon/hwmon*; do for label in \"$mon\"/temp*_label; do if [ -f $label ]; then echo $(cat \"$label\")___$(cat \"${label%_*}_input\"); fi; done; done;\nexit\n");
+    child.waitExit();
+    if(child.stdout.str.trim()!='')
+    {
+        var lines = child.stdout.str.trim().split('\n');
+        for (var i = 0; i < lines.length; ++i)
+        {
+            var line = lines[i].trim().split('___');
+            ret.push({ CurrentTemperature: (parseFloat(line[1])/1000), InstanceName: line[0] });
+        }
+    }
+    return (ret);
+}
+
+function macos_thermals()
+{
+    var ret = [];
+    var child = require('child_process').execFile('/bin/sh', ['sh']);
+    child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stderr.on('data', function () { });
+    child.stdin.write('powermetrics --help | grep SMC\nexit\n');
+    child.waitExit();
+    if (child.stdout.str.trim() != '')
+    {
+        child = require('child_process').execFile('/bin/sh', ['sh']);
+        child.stdout.str = ''; child.stdout.on('data', function (c)
+        {
+            this.str += c.toString();
+            var tokens = this.str.trim().split('\n');
+            for (var i in tokens)
+            {
+                if (tokens[i].split(' die temperature: ').length > 1)
+                {
+                    ret.push({CurrentTemperature: tokens[i].split(' ')[3], InstanceName: tokens[i].split(' ')[0]});
+                    this.parent.kill();
+                }
+            }
+        });
+        child.stderr.on('data', function (c) {
+            if (c.toString().split('unable to get smc values').length > 1) { // error getting sensors so just kill
+                this.parent.kill();
+                return;
+            }
+        });
+        child.stdin.write('powermetrics -s smc -i 500 -n 1\n');
+        child.waitExit(2000);
+    }
+    return (ret);
+}
+
+switch(process.platform)
+{
+    case 'linux':
+        module.exports = { cpuUtilization: linux_cpuUtilization, memUtilization: linux_memUtilization, thermals: linux_thermals };
+        break;
+    case 'win32':
+        module.exports = { cpuUtilization: windows_cpuUtilization, memUtilization: windows_memUtilization, thermals: windows_thermals };
+        break;
+    case 'darwin':
+        module.exports = { cpuUtilization: macos_cpuUtilization, memUtilization: macos_memUtilization, thermals: macos_thermals };
+        break;
+}
+

+ 195 - 0
agents/modules_meshcore/util-agentlog.js

@@ -0,0 +1,195 @@
+/*
+Copyright 2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+function parseLine(entry)
+{
+    var test = entry.match(/^\[.*M\]/);
+    if (test == null)
+    {
+        test = entry.match(/\[.+ => .+:[0-9]+\]/);
+        if (test != null)
+        {
+            // Windows Crash Entry
+            var file = test[0].substring(1).match(/(?!.+ ).+(?=:)/);
+            var line = test[0].match(/(?!:)[0-9]+(?=\]$)/);
+            var fn = test[0].match(/(?!\[).+(?= =>)/);
+
+            if (file != null) { this.results.peek().f = file[0].trim(); }
+            if (line != null) { this.results.peek().l = line[0]; }
+            if (fn != null) { this.results.peek().fn = fn[0]; }
+        }
+        else
+        {
+            test = entry.match(/^[\.\/].+\(\) \[0x[0-9a-fA-F]+\]$/);
+            if (test != null)
+            {
+                // Linux Crash Stack with no symbols
+                test = test[0].match(/(?!\[)0x[0-9a-fA-F]+(?=\]$)/);
+                if (test != null)
+                {
+                    if (this.results.peek().sx == null) { this.results.peek().sx = []; }
+                    this.results.peek().sx.unshift(test[0]);
+                }
+            }
+            else
+            {
+                test = entry.match(/^\[.+_[0-9a-fA-F]{16}\]$/);
+                if(test!=null)
+                {
+                    // Linux Crash ID
+                    test = test[0].match(/(?!_)[0-9a-fA-F]{16}(?=\]$)/);
+                    this.results.peek().h = test[0];
+                }
+            }
+
+            test = entry.match(/(?!^=>)\/+.+:[0-9]+$/);
+            if(test!=null)
+            {
+                // Linux Crash Entry
+                if (this.results.peek().s == null) { this.results.peek().s = []; }
+                this.results.peek().s.unshift(test[0]);
+            }
+            
+        }
+        return;
+    }
+    test = test[0];
+
+    var dd = test.substring(1, test.length -1);
+    var c = dd.split(' ');
+    var t = c[1].split(':');
+    if (c[2] == 'PM') { t[0] = parseInt(t[0]) + 12; if (t[0] == 24) { t[0] = 0; } }
+
+    var d = Date.parse(c[0] + 'T' + t.join(':'));
+    var msg = entry.substring(test.length).trim();
+    var hash = msg.match(/^\[[0-9a-fA-F]{16}\]/);
+    if (hash != null)
+    {
+        hash = hash[0].substring(1, hash[0].length - 1);
+        msg = msg.substring(hash.length + 2).trim();
+    }
+    else
+    {
+        hash = msg.match(/^\[\]/);
+        if(hash!=null)
+        {
+            msg = msg.substring(2).trim();
+            hash = null;
+        }
+    }
+
+    var log = { t: Math.floor(d / 1000), m: msg };
+    if (hash != null) { log.h = hash; }
+
+    // Check for File/Line in generic log entry
+    test = msg.match(/^.+:[0-9]+ \([0-9]+,[0-9]+\)/);
+    if (test != null)
+    {
+        log.m = log.m.substring(test[0].length).trim();
+        log.f = test[0].match(/^.+(?=:[0-9]+)/)[0];
+        log.l = test[0].match(/(?!:)[0-9]+(?= \([0-9]+,[0-9]+\)$)/)[0];
+    }
+
+    this.results.push(log);
+}
+
+function readLog_data(buffer)
+{
+    var lines = buffer.toString();
+    if (this.buffered != null) { lines = this.buffered + lines; }
+    lines = lines.split('\n');
+    var i;
+
+    for (i = 0; i < (lines.length - 1) ; ++i)
+    {
+        parseLine.call(this, lines[i]);
+    }
+
+    if (lines.length == 1)
+    {
+        parseLine.call(this, lines[0]);
+        this.buffered = null;
+    }
+    else
+    {
+        this.buffered = lines[lines.length - 1];
+    }
+}
+
+function readLogEx(path)
+{
+    var ret = [];
+    try
+    {
+        var s = require('fs').createReadStream(path);
+        s.buffered = null;
+        s.results = ret;
+        s.on('data', readLog_data);
+        s.resume();
+        if (s.buffered != null) { readLog_data.call(s, s.buffered); s.buffered = null; }
+        s.removeAllListeners('data');
+        s = null;
+    }
+    catch(z)
+    {
+    }
+
+    return (ret);
+}
+
+function readLog(criteria, path)
+{
+    var objects = readLogEx(path == null ? (process.execPath.split('.exe').join('') + '.log') : path);
+    var ret = [];
+
+    if (typeof (criteria) == 'string')
+    {
+        try
+        {
+            var dstring = Date.parse(criteria);
+            criteria = Math.floor(dstring / 1000);
+        }
+        catch(z)
+        {
+        }
+    }
+
+    if (typeof (criteria) == 'number')
+    {
+        if(criteria < 1000)
+        {
+            // Return the last xxx entries
+            ret = objects.slice(objects.length - ((criteria > objects.length) ? objects.length : criteria));
+        }
+        else
+        {
+            // Return entries that are newer than xxx
+            var i;
+            for (i = 0; i < objects.length && objects[i].t <= criteria; ++i) { }
+            ret = objects.slice(i);
+        }
+    }
+    else
+    {
+        ret = objects;
+    }
+
+    return (ret);
+}
+
+module.exports = { read: readLog, readEx: readLogEx }
+

+ 170 - 0
agents/modules_meshcore/wifi-scanner-windows.js

@@ -0,0 +1,170 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+function _Scan()
+{
+    var wlanInterfaces = this.Marshal.CreatePointer();
+    this.Native.WlanEnumInterfaces(this.Handle, 0, wlanInterfaces);
+
+    var count = wlanInterfaces.Deref().Deref(0, 4).toBuffer().readUInt32LE(0);
+
+    var info = wlanInterfaces.Deref().Deref(8, 532);
+    var iname = info.Deref(16, 512).AnsiString;
+
+    var istate;
+    switch (info.Deref(528, 4).toBuffer().readUInt32LE(0))
+    {
+        case 0:
+            istate = "NOT READY";
+            break;
+        case 1:
+            istate = "CONNECTED";
+            break;
+        case 2:
+            istate = "AD-HOC";
+            break;
+        case 3:
+            istate = "DISCONNECTING";
+            break;
+        case 4:
+            istate = "DISCONNECTED";
+            break;
+        case 5:
+            istate = "ASSOCIATING";
+            break;
+        case 6:
+            istate = "DISCOVERING";
+            break;
+        case 7:
+            istate = "AUTHENTICATING";
+            break;
+        default:
+            istate = "UNKNOWN";
+            break;
+    }
+
+    var iguid = info.Deref(0, 16);
+    if (this.Native.WlanScan(this.Handle, iguid, 0, 0, 0).Val == 0)
+    {
+        return (true);
+    }
+    else
+    {
+        return (false);
+    }
+}
+
+function AccessPoint(_ssid, _bssid, _rssi, _lq)
+{
+    this.ssid = _ssid;
+    this.bssid = _bssid;
+    this.rssi = _rssi;
+    this.lq = _lq;
+}
+AccessPoint.prototype.toString = function()
+{
+    return (this.ssid + " [" + this.bssid + "]: " + this.lq);
+}
+
+function OnNotify(NotificationData)
+{
+    var NotificationSource = NotificationData.Deref(0, 4).toBuffer().readUInt32LE(0);
+    var NotificationCode = NotificationData.Deref(4, 4).toBuffer().readUInt32LE(0);
+    var dataGuid = NotificationData.Deref(8, 16);
+
+    if ((NotificationSource & 0X00000008) && (NotificationCode == 7))
+    {
+        var bss = this.Parent.Marshal.CreatePointer();
+        var result = this.Parent.Native.GetBSSList(this.Parent.Handle, dataGuid, 0, 3, 0, 0, bss).Val;
+        if (result == 0)
+        {
+            var totalSize = bss.Deref().Deref(0, 4).toBuffer().readUInt32LE(0);
+            var numItems = bss.Deref().Deref(4, 4).toBuffer().readUInt32LE(0);
+            for (i = 0; i < numItems; ++i)
+            {
+                var item = bss.Deref().Deref(8 + (360 * i), 360);
+                var ssid = item.Deref(4, 32).String.trim();
+                var bssid = item.Deref(40, 6).HexString2;
+                var rssi = item.Deref(56, 4).toBuffer().readUInt32LE(0);
+                var lq = item.Deref(60, 4).toBuffer().readUInt32LE(0);
+
+                this.Parent.emit('Scan', new AccessPoint(ssid, bssid, rssi, lq));
+            }
+        }
+
+    }
+}
+
+function Wireless()
+{
+    var emitterUtils = require('events').inherits(this);
+
+    this.Marshal = require('_GenericMarshal');
+    this.Native = this.Marshal.CreateNativeProxy("wlanapi.dll");
+    this.Native.CreateMethod("WlanOpenHandle");
+    this.Native.CreateMethod("WlanGetNetworkBssList", "GetBSSList");
+    this.Native.CreateMethod("WlanRegisterNotification");
+    this.Native.CreateMethod("WlanEnumInterfaces");
+    this.Native.CreateMethod("WlanScan");
+    this.Native.CreateMethod("WlanQueryInterface");
+
+    var negotiated = this.Marshal.CreatePointer();
+    var h = this.Marshal.CreatePointer();
+
+    this.Native.WlanOpenHandle(2, 0, negotiated, h);
+    this.Handle = h.Deref();
+
+    this._NOTIFY_PROXY_OBJECT = this.Marshal.CreateCallbackProxy(OnNotify, 2);
+    this._NOTIFY_PROXY_OBJECT.Parent = this;
+    var PrevSource = this.Marshal.CreatePointer();
+    var result = this.Native.WlanRegisterNotification(this.Handle, 0X0000FFFF, 0, this._NOTIFY_PROXY_OBJECT.Callback, this._NOTIFY_PROXY_OBJECT.State, 0, PrevSource);
+
+    emitterUtils.createEvent('Scan');
+    emitterUtils.addMethod('Scan', _Scan);
+
+    this.GetConnectedNetwork = function ()
+    {
+        var interfaces = this.Marshal.CreatePointer();
+
+        console.log('Success = ' + this.Native.WlanEnumInterfaces(this.Handle, 0, interfaces).Val);
+        var count = interfaces.Deref().Deref(0, 4).toBuffer().readUInt32LE(0);
+        var info = interfaces.Deref().Deref(8, 532);
+        var iname = info.Deref(16, 512).AnsiString;
+        var istate = info.Deref(528, 4).toBuffer().readUInt32LE(0);
+        if(info.Deref(528, 4).toBuffer().readUInt32LE(0) == 1) // CONNECTED
+        {
+            var dataSize = this.Marshal.CreatePointer();
+            var pData = this.Marshal.CreatePointer();
+            var valueType = this.Marshal.CreatePointer();
+            var iguid = info.Deref(0, 16);
+            var retVal = this.Native.WlanQueryInterface(this.Handle, iguid, 7, 0, dataSize, pData, valueType).Val;
+            if (retVal == 0)
+            {
+                var associatedSSID = pData.Deref().Deref(524, 32).String;
+                var bssid = pData.Deref().Deref(560, 6).HexString;
+                var lq = pData.Deref().Deref(576, 4).toBuffer().readUInt32LE(0);
+
+                return (new AccessPoint(associatedSSID, bssid, 0, lq));
+            }
+        }
+        throw ("GetConnectedNetworks: FAILED (not associated to a network)");
+    };
+
+
+    return (this);
+}
+
+module.exports = new Wireless();

+ 127 - 0
agents/modules_meshcore/wifi-scanner.js

@@ -0,0 +1,127 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+var MemoryStream = require('MemoryStream');
+var WindowsChildScript = 'var parent = require("ScriptContainer");var Wireless = require("wifi-scanner-windows");Wireless.on("Scan", function (ap) { parent.send(ap); });Wireless.Scan();';
+
+
+function AccessPoint(_ssid, _bssid, _lq)
+{
+    this.ssid = _ssid;
+    this.bssid = _bssid;
+    this.lq = _lq;
+}
+AccessPoint.prototype.toString = function ()
+{
+    return ("[" + this.bssid + "]: " + this.ssid + " (" + this.lq + ")");
+    //return (this.ssid + " [" + this.bssid + "]: " + this.lq);
+}
+
+function WiFiScanner()
+{
+    var emitterUtils = require('events').inherits(this);
+    emitterUtils.createEvent('accessPoint');
+
+    this.hasWireless = function ()
+    {
+        var retVal = false;
+        var interfaces = require('os').networkInterfaces();
+        for (var name in interfaces)
+        {
+            if (interfaces[name][0].type == 'wireless') { retVal = true; break; }
+        }
+        return (retVal);
+    };
+
+    this.Scan = function ()
+    {
+        if (process.platform == 'win32')
+        {
+            this.main = require('ScriptContainer').Create(15, ContainerPermissions.DEFAULT);
+            this.main.parent = this;
+            this.main.on('data', function (j) { this.parent.emit('accessPoint', new AccessPoint(j.ssid, j.bssid, j.lq)); });
+
+            this.main.addModule('wifi-scanner-windows', getJSModule('wifi-scanner-windows'));
+            this.main.ExecuteString(WindowsChildScript);
+        }
+        else if (process.platform == 'linux')
+        {
+            // Need to get the wireless interface name
+            var interfaces = require('os').networkInterfaces();
+            var wlan = null;
+            for (var i in interfaces)
+            {
+                if (interfaces[i][0].type == 'wireless')
+                {
+                    wlan = i;
+                    break;
+                }
+            }
+            if (wlan != null)
+            {
+                this.child = require('child_process').execFile('/sbin/iwlist', ['iwlist', wlan, 'scan']);
+                this.child.parent = this;
+                this.child.ms = new MemoryStream();
+                this.child.ms.parent = this.child;
+                this.child.stdout.on('data', function (buffer) { this.parent.ms.write(buffer); });
+                this.child.on('exit', function () { this.ms.end(); });
+                this.child.ms.on('end', function ()
+                {
+                    var str = this.buffer.toString();
+                    tokens = str.split(' - Address: ');
+                    for (var block in tokens)
+                    {
+                        if (block == 0) continue;
+                        var ln = tokens[block].split('\n');
+                        var _bssid = ln[0];
+                        var _lq;
+                        var _ssid;
+
+                        for (var lnblock in ln)
+                        {
+                            lnblock = ln[lnblock].trim();
+                            lnblock = lnblock.trim();
+                            if (lnblock.startsWith('ESSID:'))
+                            {
+                                _ssid = lnblock.slice(7, lnblock.length - 1);
+                                if (_ssid == '<hidden>') { _ssid = ''; }
+                            }
+                            if (lnblock.startsWith('Signal level='))
+                            {
+                                _lq = lnblock.slice(13,lnblock.length-4);
+                            }
+                            else if (lnblock.startsWith('Quality='))
+                            {
+                                _lq = lnblock.slice(8, 10);
+                                var scale = lnblock.slice(11, 13);
+                            }
+                        }
+                        this.parent.parent.emit('accessPoint', new AccessPoint(_ssid, _bssid, _lq));
+                    }
+                });
+            }
+        }
+    }
+}
+
+module.exports = WiFiScanner;
+
+
+
+
+
+
+

+ 163 - 0
agents/modules_meshcore/win-console.js

@@ -0,0 +1,163 @@
+/*
+Copyright 2018-2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+var TrayIconFlags =
+    {
+        NIF_MESSAGE: 0x00000001,
+        NIF_ICON: 0x00000002,
+        NIF_TIP: 0x00000004,
+        NIF_STATE: 0x00000008,
+        NIF_INFO: 0x00000010,
+        NIF_GUID: 0x00000020,
+        NIF_REALTIME: 0x00000040,
+        NIF_SHOWTIP: 0x00000080,
+
+        NIM_ADD: 0x00000000,
+        NIM_MODIFY: 0x00000001,
+        NIM_DELETE: 0x00000002,
+        NIM_SETFOCUS: 0x00000003,
+        NIM_SETVERSION: 0x00000004
+    };
+var NOTIFYICON_VERSION_4 = 4;
+var MessageTypes = { WM_APP: 0x8000, WM_USER: 0x0400 };
+function WindowsConsole()
+{
+    if (process.platform == 'win32')
+    {
+        this._ObjectID = 'win-console';
+        this._Marshal = require('_GenericMarshal');
+        this._kernel32 = this._Marshal.CreateNativeProxy("kernel32.dll");
+        this._user32 = this._Marshal.CreateNativeProxy("user32.dll");
+        this._kernel32.CreateMethod("GetConsoleWindow");
+        this._kernel32.CreateMethod('GetCurrentThread');
+        this._user32.CreateMethod("ShowWindow");
+        this._user32.CreateMethod("LoadImageA");
+        this._user32.CreateMethod({ method: 'GetMessageA', threadDispatch: 1 });
+        this._shell32 = this._Marshal.CreateNativeProxy('Shell32.dll');
+        this._shell32.CreateMethod('Shell_NotifyIconA');
+
+        this._handle = this._kernel32.GetConsoleWindow();
+        this.minimize = function () {
+            this._user32.ShowWindow(this._handle, 6);
+        };
+        this.restore = function () {
+            this._user32.ShowWindow(this._handle, 9);
+        };
+        this.hide = function () {
+            this._user32.ShowWindow(this._handle, 0);
+        };
+        this.show = function () {
+            this._user32.ShowWindow(this._handle, 5);
+        };
+
+
+        this._loadicon = function (imagePath) {
+            var h = this._user32.LoadImageA(0, this._Marshal.CreateVariable(imagePath), 1, 0, 0, 0x00000010 | 0x00008000 | 0x00000040); // LR_LOADFROMFILE | LR_SHARED | LR_DEFAULTSIZE
+            return (h);
+        };
+
+        this.SetTrayIcon = function SetTrayIcon(options)
+        {
+            var data = this._Marshal.CreateVariable(this._Marshal.PointerSize == 4 ? 508 : 528);
+            //console.log('struct size = ' + data._size);
+            //console.log('TryIcon, WM_MESSAGE filter = ' + options.filter);
+            data.toBuffer().writeUInt32LE(data._size, 0);
+
+            var trayType = TrayIconFlags.NIF_TIP | TrayIconFlags.NIF_MESSAGE
+            options.filter = MessageTypes.WM_APP + 1;
+            data.Deref(this._Marshal.PointerSize == 4 ? 16 : 24, 4).toBuffer().writeUInt32LE(options.filter);
+
+            if (!options.noBalloon) { trayType |= TrayIconFlags.NIF_INFO; }
+
+            if (options.icon)
+            {                
+                trayType |= TrayIconFlags.NIF_ICON;
+                var hIcon = data.Deref(this._Marshal.PointerSize == 4 ? 20 : 32, this._Marshal.PointerSize);
+                options.icon.pointerBuffer().copy(hIcon.toBuffer());
+            }
+
+            data.Deref(this._Marshal.PointerSize * 2, 4).toBuffer().writeUInt32LE(1);
+            data.Deref(this._Marshal.PointerSize == 4 ? 12 : 20, 4).toBuffer().writeUInt32LE(trayType);
+            data.Deref(this._Marshal.PointerSize == 4 ? 416 : 432, 4).toBuffer().writeUInt32LE(NOTIFYICON_VERSION_4);
+
+            var szTip = data.Deref(this._Marshal.PointerSize == 4 ? 24 : 40, 128);
+            var szInfo = data.Deref(this._Marshal.PointerSize == 4 ? 160 : 176, 256);
+            var szInfoTitle = data.Deref(this._Marshal.PointerSize == 4 ? 420 : 436, 64);
+
+            if (options.szTip) { Buffer.from(options.szTip).copy(szTip.toBuffer()); }
+            if (options.szInfo) { Buffer.from(options.szInfo).copy(szInfo.toBuffer()); }
+            if (options.szInfoTitle) { Buffer.from(options.szInfoTitle).copy(szInfoTitle.toBuffer()); }
+
+
+            var MessagePump = require('win-message-pump');
+            retVal = { _ObjectID: 'WindowsConsole.TrayIcon', MessagePump: new MessagePump(options) };
+            var retValEvents = require('events').inherits(retVal);
+            retValEvents.createEvent('ToastClicked');
+            retValEvents.createEvent('IconHover');
+            retValEvents.createEvent('ToastDismissed');
+            retVal.Options = options;
+            retVal.MessagePump.TrayIcon = retVal;
+            retVal.MessagePump.NotifyData = data;
+            retVal.MessagePump.WindowsConsole = this;
+            retVal.MessagePump.on('exit', function onExit(code) { console.log('Pump Exited'); if (this.TrayIcon) { this.TrayIcon.remove(); } });
+            retVal.MessagePump.on('hwnd', function onHwnd(h)
+            {
+                //console.log('Got HWND');
+                options.hwnd = h;
+                h.pointerBuffer().copy(this.NotifyData.Deref(this.WindowsConsole._Marshal.PointerSize, this.WindowsConsole._Marshal.PointerSize).toBuffer());
+
+                if(this.WindowsConsole._shell32.Shell_NotifyIconA(TrayIconFlags.NIM_ADD, this.NotifyData).Val == 0)
+                {
+                    // Something went wrong
+                }
+            });
+            retVal.MessagePump.on('message', function onWindowsMessage(msg)
+            {
+                if(msg.message == this.TrayIcon.Options.filter)
+                {
+                    var handled = false;
+                    if (msg.wparam == 1 && msg.lparam == 1029)
+                    {
+                        this.TrayIcon.emit('ToastClicked');
+                        handled = true;
+                    }
+                    if (msg.wparam == 1 && msg.lparam == 512)
+                    {
+                        this.TrayIcon.emit('IconHover');
+                        handled = true;
+                    }
+                    if (this.TrayIcon.Options.balloonOnly && msg.wparam == 1 && (msg.lparam == 1028 || msg.lparam == 1029))
+                    {
+                        this.TrayIcon.emit('ToastDismissed');
+                        this.TrayIcon.remove();
+                        handled = true;
+                    }
+                }
+            });
+            retVal.remove = function remove()
+            {
+                this.MessagePump.WindowsConsole._shell32.Shell_NotifyIconA(TrayIconFlags.NIM_DELETE, this.MessagePump.NotifyData);
+                this.MessagePump.stop();
+                delete this.MessagePump.TrayIcon;
+                delete this.MessagePump;
+            };
+            return (retVal);
+            
+        };
+    }
+}
+
+module.exports = new WindowsConsole();

+ 194 - 0
agents/modules_meshcore/win-deskutils.js

@@ -0,0 +1,194 @@
+/*
+Copyright 2022 Intel Corporation
+@author Bryan Roe
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+//
+// win-deskutils is a utility module that exposes various desktop related features for Windows
+// such as MouseTrails Accessability and Windows Desktop Background
+//
+
+//
+// MSDN documention for the system call this module relies on can be found at:
+// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa
+//
+
+var SPI_GETDESKWALLPAPER = 0x0073;
+var SPI_SETDESKWALLPAPER = 0x0014;
+var SPI_GETMOUSETRAILS = 0x005E;
+var SPI_SETMOUSETRAILS = 0x005D;
+
+var GM = require('_GenericMarshal');
+var user32 = GM.CreateNativeProxy('user32.dll');
+user32.CreateMethod('SystemParametersInfoA');
+
+//
+// This function is a helper method to dispatch method calls to different user sessions
+//
+function sessionDispatch(tsid, parent, method, args)
+{
+    //
+    // Check to see if the process owner of the current processor is root
+    //
+    var sid = undefined;
+    var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
+    /*
+        The following is the list of possible values for stype.
+        If the current process owner is root, we set the stype to user,
+        because we cannot set/get any properties from this user, we
+        must switch to a user session.. Default behavior for stype(1)
+        is that it will context switch to the logged in user. If
+        this is not intended, then an actual user TSID must be specified, using
+        ILibProcessPipe_SpawnTypes_SPECIFIED_USER and the actual TSID
+        ------------------------------------------------------------------------
+        ILibProcessPipe_SpawnTypes_DEFAULT = 0,
+        ILibProcessPipe_SpawnTypes_USER = 1,
+        ILibProcessPipe_SpawnTypes_WINLOGON = 2,
+        ILibProcessPipe_SpawnTypes_TERM = 3,
+        ILibProcessPipe_SpawnTypes_DETACHED = 4,
+        ILibProcessPipe_SpawnTypes_SPECIFIED_USER = 5,
+        ILibProcessPipe_SpawnTypes_POSIX_DETACHED = 0x8000
+        ------------------------------------------------------------------------
+    */
+    console.log('stype: ' + stype);
+    if (stype == 1)
+    {
+        if (tsid == null && require('MeshAgent')._tsid != null)
+        {
+            stype = 5;                          // ILibProcessPipe_SpawnTypes_SPECIFIED_USER
+            sid = require('MeshAgent')._tsid;   // If this is set, it was set via user selection UI
+        }
+        else
+        {
+            sid = tsid;                         // Set the SID to be whatever was passed in
+        }
+    }
+
+    // Spawn a child process in the appropriate user session, and relay the response back via stdout
+    var mod = Buffer.from(getJSModule('win-deskutils')).toString('base64');
+    var prog = "try { addModule('win-deskutils', process.env['win_deskutils']);} catch (x) { } var x;try{x=require('win-deskutils').dispatch('" + parent + "', '" + method + "', " + JSON.stringify(args) + ");console.log(x);}catch(z){console.log(z);process.exit(1);}process.exit(0);";
+    var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', Buffer.from(prog).toString('base64')], { type: stype, uid: sid, env: { win_deskutils: getJSModule('win-deskutils') } });
+
+    child.stdout.str = '';
+    child.stdout.on('data', function (c) { this.str += c.toString(); });
+    child.stderr.on('data', function (c) { });
+    child.on('exit', function (c) { this.exitCode = c; });
+    child.waitExit();
+    if (child.exitCode == 0)
+    {
+        return (child.stdout.str.trim()); // If the return code was 0, then relay the response from stdout
+    }
+    else
+    {
+        throw (child.stdout.str.trim()); // If the return code was nonzero, then the stdout response is the exception that should be bubbled
+    }
+}
+
+//
+// This function gets the path of the windows desktop background of the specified user desktop session
+//
+function background_get(tsid)
+{
+    if (tsid != null || tsid === null) // TSID is not undefined or is explicitly null
+    {
+        // Need to disatch to different session first
+        return (sessionDispatch(tsid, 'background', 'get', []));
+    }
+    var v = GM.CreateVariable(1024);
+    var ret = user32.SystemParametersInfoA(SPI_GETDESKWALLPAPER, v._size, v, 0);
+    if (ret.Val == 0)
+    {
+        throw ('Error occured trying to fetch wallpaper');
+    }
+    return (v.String);
+}
+
+//
+// This function sets the path for the windows desktop background of the specified user desktop session
+//
+function background_set(path, tsid)
+{
+    if (tsid != null || tsid === null) // TSID is not undefined or is explicitly null
+    {
+        // Need to disatch to different session first
+        return (sessionDispatch(tsid, 'background', 'set', [path]));
+    }
+    var nb = GM.CreateVariable(path);
+    var ret = user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER, nb._size, nb, 0);
+    if (ret.Val == 0)
+    {
+        throw ('Error occured trying to set wallpaper');
+    }
+    return;
+}
+
+//
+// This is a helper function that is called by the child process from sessionDispatch()
+//
+function dispatch(parent, method, args)
+{
+    try
+    {
+        return (this[parent][method].apply(this, args));
+    }
+    catch (e)
+    {
+        console.log('ERROR: ' + e);
+        throw ('Error occured trying to dispatch: ' + method);
+    }
+}
+
+//
+// This function sets the mousetrail accessibility feature, for the specified user desktop session.
+// Setting value 0 or one disables this feature
+// Otherwise, value is the number of cursors to render for this feature
+//
+function mousetrails_set(value, tsid)
+{
+    if (tsid != null || tsid === null) // TSID is not undefined or is explicitly null
+    {
+        // Need to disatch to different session first
+        return (sessionDispatch(tsid, 'mouse', 'setTrails', [value]));
+    }
+    var ret = user32.SystemParametersInfoA(SPI_SETMOUSETRAILS, value, 0, 0);
+    if (ret.Val == 0)
+    {
+        throw ('Error occured trying to fetch wallpaper');
+    }
+}
+
+//
+// This function returns the number of cursors the mousetrail accessibility feature will render
+// A value of 0 or 1 means the feature is disabled, otherwise it is the number of cursors that will be rendered
+//
+function mousetrails_get(tsid)
+{
+    if (tsid != null || tsid === null) // TSID is not undefined or is explicitly null
+    {
+        // Need to disatch to different session first
+        return (sessionDispatch(tsid, 'mouse', 'getTrails', []));
+    }
+    var v = GM.CreateVariable(4);
+    var ret = user32.SystemParametersInfoA(SPI_GETMOUSETRAILS, v._size, v, 0);
+    if (ret.Val == 0)
+    {
+        throw ('Error occured trying to fetch wallpaper');
+    }
+    return (v.toBuffer().readUInt32LE());
+}
+
+module.exports = { background: { get: background_get, set: background_set } };
+module.exports.mouse = { getTrails: mousetrails_get, setTrails: mousetrails_set };
+module.exports.dispatch = dispatch;

+ 280 - 0
agents/modules_meshcore/win-info.js

@@ -0,0 +1,280 @@
+/*
+Copyright 2019-2020 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+var promise = require('promise');
+
+function qfe()
+{
+    try {
+        var tokens = require('win-wmi').query('ROOT\\CIMV2', 'SELECT * FROM Win32_QuickFixEngineering');
+        if (tokens[0]){
+            for (var index = 0; index < tokens.length; index++) {
+                for (var key in tokens[index]) {
+                    if (key.startsWith('__')) delete tokens[index][key];
+                }
+            }
+            return (tokens);
+        } else {
+            return ([]);
+        }
+    } catch (ex) {
+        return ([]);
+    }
+}
+function av()
+{
+    var result = [];
+    try { 
+        var tokens = require('win-wmi-fixed').query('ROOT\\SecurityCenter2', 'SELECT * FROM AntiVirusProduct');
+        if (tokens.length == 0) { return ([]); }
+        // Process each antivirus product
+        for (var i = 0; i < tokens.length; ++i) {
+            var product = tokens[i];
+            var modifiedPath = product.pathToSignedProductExe || '';
+            // Expand environment variables (e.g., %ProgramFiles%)
+            var regex = /%([^%]+)%/g;
+            var match;
+            while ((match = regex.exec(product.pathToSignedProductExe)) !== null) {
+                var envVar = match[1];
+                var envValue = process.env[envVar] || '';
+                if (envValue) {
+                    modifiedPath = modifiedPath.replace(match[0], envValue);
+                }
+            }
+            // Check if the executable exists (unless it's Windows Defender pseudo-path)
+            var flag = true;
+            if (modifiedPath !== 'windowsdefender://') {
+                try {
+                    if (!require('fs').existsSync(modifiedPath)) {
+                        flag = false;
+                    }
+                } catch (ex) {
+                    flag = false;
+                }
+            }
+            // Only include products with valid executables
+            if (flag) {
+                var status = {};
+                status.product = product.displayName || '';
+                status.updated = (parseInt(product.productState) & 0x10) == 0;
+                status.enabled = (parseInt(product.productState) & 0x1000) == 0x1000;
+                result.push(status);
+            }
+        }
+        return (result);
+    } catch (ex) {
+        return ([]);
+    }
+}
+function defrag(options)
+{
+    var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
+    var path = '';
+
+    switch(require('os').arch())
+    {
+        case 'x64':
+            if (require('_GenericMarshal').PointerSize == 4)
+            {
+                // 32 Bit App on 64 Bit Windows
+                ret._rej('Cannot defrag volume on 64 bit Windows from 32 bit application');
+                return (ret);
+            }
+            else
+            {
+                // 64 Bit App
+                path = process.env['windir'] + '\\System32\\defrag.exe';
+            }
+            break;
+        case 'ia32':
+            // 32 Bit App on 32 Bit Windows
+            path = process.env['windir'] + '\\System32\\defrag.exe';
+            break;
+        default:
+            ret._rej(require('os').arch() + ' not supported');
+            return (ret);
+            break;
+    }
+
+    ret.child = require('child_process').execFile(process.env['windir'] + '\\System32\\defrag.exe', ['defrag', options.volume + ' /A']);
+    ret.child.promise = ret;
+    ret.child.promise.options = options;
+    ret.child.stdout.str = ''; ret.child.stdout.on('data', function (c) { this.str += c.toString(); });
+    ret.child.stderr.str = ''; ret.child.stderr.on('data', function (c) { this.str += c.toString(); });
+    ret.child.on('exit', function (code)
+    {
+        var lines = this.stdout.str.trim().split('\r\n');
+        var obj = { volume: this.promise.options.volume };
+        for (var i in lines)
+        {
+            var token = lines[i].split('=');
+            if(token.length == 2)
+            {
+                switch(token[0].trim().toLowerCase())
+                {
+                    case 'volume size':
+                        obj['size'] = token[1];
+                        break;
+                    case 'free space':
+                        obj['free'] = token[1];
+                        break;
+                    case 'total fragmented space':
+                        obj['fragmented'] = token[1];
+                        break;
+                    case 'largest free space size':
+                        obj['largestFragment'] = token[1];
+                        break;
+                }               
+            }
+        }
+        this.promise._res(obj);
+    });
+    return (ret);
+}
+function regQuery(H, Path, Key)
+{
+    try
+    {
+        return(require('win-registry').QueryKey(H, Path, Key));
+    }
+    catch(e)
+    {
+        return (null);
+    }
+}
+function pendingReboot()
+{
+    var tmp = null;
+    var ret = null;
+    var HKEY = require('win-registry').HKEY;
+    if(regQuery(HKEY.LocalMachine, 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing', 'RebootPending') !=null)
+    {
+        ret = 'Component Based Servicing';
+    }
+    else if(regQuery(HKEY.LocalMachine, 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate', 'RebootRequired'))
+    {
+        ret = 'Windows Update';
+    }
+    else if ((tmp=regQuery(HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Control\\Session Manager', 'PendingFileRenameOperations'))!=null && tmp != 0 && tmp != '')
+    {
+        ret = 'File Rename';
+    }
+    else if (regQuery(HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ActiveComputerName', 'ComputerName') != regQuery(HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ComputerName', 'ComputerName'))
+    {
+        ret = 'System Rename';
+    }
+    return (ret);
+}
+
+function installedApps()
+{
+    var promise = require('promise');
+    var ret = new promise(function (a, r) { this._resolve = a; this._reject = r; });
+    
+    var code = "\
+    var reg = require('win-registry');\
+    var result = [];\
+    var val, tmp;\
+    var items = reg.QueryKey(reg.HKEY.LocalMachine, 'SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall');\
+    for (var key in items.subkeys)\
+    {\
+        val = {};\
+        try\
+        {\
+            val.name = reg.QueryKey(reg.HKEY.LocalMachine, 'SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\' + items.subkeys[key], 'DisplayName');\
+        }\
+        catch(e)\
+        {\
+            continue;\
+        }\
+        try\
+        {\
+            val.version = reg.QueryKey(reg.HKEY.LocalMachine, 'SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\' + items.subkeys[key], 'DisplayVersion');\
+            if (val.version == '') { delete val.version; }\
+        }\
+        catch(e)\
+        {\
+        }\
+        try\
+        {\
+            val.location = reg.QueryKey(reg.HKEY.LocalMachine, 'SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\' + items.subkeys[key], 'InstallLocation');\
+            if (val.location == '') { delete val.location; }\
+        }\
+        catch(e)\
+        {\
+        }\
+        try\
+        {\
+            val.installdate = reg.QueryKey(reg.HKEY.LocalMachine, 'SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\' + items.subkeys[key], 'InstallDate');\
+            if (val.installdate == '') { delete val.installdate; }\
+        }\
+        catch(e)\
+        {\
+        }\
+        result.push(val);\
+    }\
+    console.log(JSON.stringify(result,'', 1));process.exit();";
+
+    ret.child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop().split('.exe')[0], '-exec "' + code + '"']);
+    ret.child.promise = ret;
+    ret.child.stdout.str = ''; ret.child.stdout.on('data', function (c) { this.str += c.toString(); });
+    ret.child.on('exit', function (c) { this.promise._resolve(JSON.parse(this.stdout.str.trim())); });
+    return (ret);
+}
+
+function installedStoreApps(){
+    try {
+        var tokens = require('win-wmi-fixed').query('ROOT\\CIMV2', 'SELECT * FROM Win32_InstalledStoreProgram');
+        if (tokens[0]){
+            for (var index = 0; index < tokens.length; index++) {
+                for (var key in tokens[index]) {
+                    if (key.startsWith('__')) delete tokens[index][key];
+                }
+            }
+            return (tokens);
+        } else {
+            return ([]);
+        };
+    } catch (ex) {
+        return ([]);
+    }
+}
+
+function defender(){
+    try {
+        var tokens = require('win-wmi').query('ROOT\\Microsoft\\Windows\\Defender', 'SELECT * FROM MSFT_MpComputerStatus', ['RealTimeProtectionEnabled','IsTamperProtected','AntivirusSignatureVersion','AntivirusSignatureLastUpdated']);
+        if (tokens[0]){
+            var info = { RealTimeProtection: tokens[0].RealTimeProtectionEnabled, TamperProtected: tokens[0].IsTamperProtected };
+            if (tokens[0].AntivirusSignatureVersion) { info.AntivirusSignatureVersion = tokens[0].AntivirusSignatureVersion; }
+            if (tokens[0].AntivirusSignatureLastUpdated) { info.AntivirusSignatureLastUpdated = tokens[0].AntivirusSignatureLastUpdated; }
+            return (info);
+        } else {
+            return ({});
+        }
+    } catch (ex) {
+        return ({});
+    }
+}
+
+if (process.platform == 'win32')
+{
+    module.exports = { qfe: qfe, av: av, defrag: defrag, pendingReboot: pendingReboot, installedApps: installedApps, installedStoreApps: installedStoreApps, defender: defender };
+}
+else
+{
+    var not_supported = function () { throw (process.platform + ' not supported'); };
+    module.exports = { qfe: not_supported, av: not_supported, defrag: not_supported, pendingReboot: not_supported, installedApps: not_supported, installedStoreApps: not_supported, defender: not_supported };
+}

+ 101 - 0
agents/modules_meshcore/win-securitycenter.js

@@ -0,0 +1,101 @@
+/*
+Copyright 2021 Intel Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+var seccenter = null;
+var WSC_SECURITY_PROVIDER_FIREWALL = 0x1;
+var WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS = 0x2;
+var WSC_SECURITY_PROVIDER_ANTIVIRUS = 0x4;
+var WSC_SECURITY_PROVIDER_ANTISPYWARE = 0x8;
+
+var WSC_SECURITY_PROVIDER_HEALTH_GOOD = 0;          // Green pillar in English locales
+var WSC_SECURITY_PROVIDER_HEALTH_NOTMONITORED = 1;  // Yellow pillar in English locales
+var WSC_SECURITY_PROVIDER_HEALTH_POOR = 2;          // Red pillar in English locales
+var WSC_SECURITY_PROVIDER_HEALTH_SNOOZE = 3;        // Yellow pillar in English locales
+
+try
+{
+    seccenter = require('_GenericMarshal').CreateNativeProxy('Wscapi.dll');
+    seccenter.CreateMethod('WscGetSecurityProviderHealth');
+    seccenter.CreateMethod('WscRegisterForChanges');
+    seccenter.CreateMethod('WscUnRegisterChanges'); 
+}
+catch(e)
+{
+}
+
+function statusString(val)
+{
+    var ret = 'UNKNOWN';
+
+    switch (val)
+    {
+        case 0:
+            ret = 'OK';
+            break;
+        case 1:
+        case 3:
+            ret = 'WARNING';
+            break;
+        case 2:
+            ret = 'PROBLEM';
+            break;
+        default:
+            ret = 'UNKNOWN';
+            break;
+    }
+    return (ret);
+}
+function getStatus()
+{
+    var ret = { firewall: 'UNKNOWN', antiVirus: 'UNKNOWN', autoUpdate: 'UNKNOWN' };
+    if (seccenter != null)
+    {
+        var status = require('_GenericMarshal').CreateVariable(4);
+        if (seccenter.WscGetSecurityProviderHealth(WSC_SECURITY_PROVIDER_FIREWALL, status).Val == 0) { ret.firewall = statusString(status.toBuffer().readUInt32LE()); }
+        if (seccenter.WscGetSecurityProviderHealth(WSC_SECURITY_PROVIDER_ANTIVIRUS, status).Val == 0) { ret.antiVirus = statusString(status.toBuffer().readUInt32LE()); }
+        if (seccenter.WscGetSecurityProviderHealth(WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS, status).Val == 0) { ret.autoUpdate = statusString(status.toBuffer().readUInt32LE()); }
+    }
+    return (ret);
+}
+
+if (process.platform == 'win32' && seccenter != null)
+{
+    var j = { status: getStatus };
+    require('events').EventEmitter.call(j, true)
+        .createEvent('changed');
+    j._H = require('_GenericMarshal').CreatePointer();
+    j._EV = require('_GenericMarshal').GetGenericGlobalCallback(1);
+    j._EV.parent = j;
+    j._EV.on('GlobalCallback', function (p)
+    {
+        if (!this.ObjectToPtr_Verify(this.parent, p)) { return; } // This event is not for us
+        this.parent.emit('changed');
+    });
+    j.on('~', function ()
+    {
+        if (seccenter.WscUnRegisterChanges(this._H).Val == 0) { }
+    });
+
+    if (seccenter.WscRegisterForChanges(0, j._H, j._EV, require('_GenericMarshal').ObjectToPtr(j)).Val == 0)
+    {
+        j._H = j._H.Deref();
+    }
+    module.exports = j;
+}
+else
+{
+    throw ('win-securitycenter not supported on this platform');
+}

+ 721 - 0
agents/modules_meshcore/win-terminal.js

@@ -0,0 +1,721 @@
+/*
+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.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+var promise = require('promise');
+var duplex = require('stream').Duplex;
+
+var SW_HIDE = 0;
+var SW_MINIMIZE = 6;
+var STARTF_USESHOWWINDOW = 0x1;
+var STD_INPUT_HANDLE = -10;
+var STD_OUTPUT_HANDLE = -11;
+var EVENT_CONSOLE_CARET = 0x4001;
+var EVENT_CONSOLE_END_APPLICATION = 0x4007;
+var WINEVENT_OUTOFCONTEXT = 0x000;
+var WINEVENT_SKIPOWNPROCESS = 0x0002;
+var CREATE_NEW_PROCESS_GROUP = 0x200;
+var EVENT_CONSOLE_UPDATE_REGION = 0x4002;
+var EVENT_CONSOLE_UPDATE_SIMPLE = 0x4003;
+var EVENT_CONSOLE_UPDATE_SCROLL = 0x4004;
+var EVENT_CONSOLE_LAYOUT = 0x4005;
+var EVENT_CONSOLE_START_APPLICATION = 0x4006;
+var KEY_EVENT = 0x1;
+var MAPVK_VK_TO_VSC = 0;
+var WM_QUIT = 0x12;
+
+var GM = require('_GenericMarshal');
+var si = GM.CreateVariable(GM.PointerSize == 4 ? 68 : 104);
+var pi = GM.CreateVariable(GM.PointerSize == 4 ? 16 : 24);
+
+si.Deref(0, 4).toBuffer().writeUInt32LE(GM.PointerSize == 4 ? 68 : 104);                    // si.cb
+si.Deref(GM.PointerSize == 4 ? 48 : 64, 2).toBuffer().writeUInt16LE(SW_HIDE | SW_MINIMIZE); // si.wShowWindow
+si.Deref(GM.PointerSize == 4 ? 44 : 60, 4).toBuffer().writeUInt32LE(STARTF_USESHOWWINDOW);  // si.dwFlags;
+
+var MSG = GM.CreateVariable(GM.PointerSize == 4 ? 28 : 48);
+
+function windows_terminal() {
+    this._ObjectID = 'windows_terminal';
+    this._user32 = GM.CreateNativeProxy('User32.dll');
+    this._user32.CreateMethod('DispatchMessageA');
+    this._user32.CreateMethod('GetMessageA');
+    this._user32.CreateMethod('MapVirtualKeyA');
+    this._user32.CreateMethod('PostThreadMessageA');
+    this._user32.CreateMethod('SetWinEventHook');
+    this._user32.CreateMethod('ShowWindow');
+    this._user32.CreateMethod('TranslateMessage');
+    this._user32.CreateMethod('UnhookWinEvent');
+    this._user32.CreateMethod('VkKeyScanA');
+    this._user32.terminal = this;
+    
+    this._kernel32 = GM.CreateNativeProxy('Kernel32.dll');
+    this._kernel32.CreateMethod('AllocConsole');
+    this._kernel32.CreateMethod('CreateProcessA');
+    this._kernel32.CreateMethod('CloseHandle');
+    this._kernel32.CreateMethod('FillConsoleOutputAttribute');
+    this._kernel32.CreateMethod('FillConsoleOutputCharacterA');
+    this._kernel32.CreateMethod('GetConsoleScreenBufferInfo');
+    this._kernel32.CreateMethod('GetConsoleWindow');
+    this._kernel32.CreateMethod('GetLastError');
+    this._kernel32.CreateMethod('GetStdHandle');
+    this._kernel32.CreateMethod('GetThreadId');
+    this._kernel32.CreateMethod('ReadConsoleOutputA');
+    this._kernel32.CreateMethod('SetConsoleCursorPosition');
+    this._kernel32.CreateMethod('SetConsoleScreenBufferSize');
+    this._kernel32.CreateMethod('SetConsoleWindowInfo');
+    this._kernel32.CreateMethod('TerminateProcess');
+    this._kernel32.CreateMethod('WaitForSingleObject');
+    this._kernel32.CreateMethod('WriteConsoleInputA');
+    
+    var currentX = 0;
+    var currentY = 0;
+    
+    this._scrx = 0;
+    this._scry = 0;
+    
+    this.SendCursorUpdate = function () {
+        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)
+        {
+            //
+            // 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 ()
+    {
+        //
+        // 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; }
+        
+        var coordScreen = GM.CreateVariable(4);
+        var dwConSize = CONSOLE_SCREEN_BUFFER_INFO.Deref(0, 2).toBuffer().readUInt16LE(0) * CONSOLE_SCREEN_BUFFER_INFO.Deref(2, 2).toBuffer().readUInt16LE(0);
+        var cCharsWritten = GM.CreateVariable(4);
+        
+        // Fill the entire screen with blanks.
+        if (this._kernel32.FillConsoleOutputCharacterA(this._stdoutput, 32, dwConSize, coordScreen.Deref(0, 4).toBuffer().readUInt32LE(), cCharsWritten).Val == 0) { return; }
+        
+        // Get the current text attribute.
+        if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO).Val == 0) { return; }
+        
+        // Set the buffer's attributes accordingly.
+        if (this._kernel32.FillConsoleOutputAttribute(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO.Deref(8, 2).toBuffer().readUInt16LE(0), dwConSize, coordScreen.Deref(0, 4).toBuffer().readUInt32LE(), cCharsWritten).Val == 0) { return; }
+        
+        // Put the cursor at its home coordinates.
+        this._kernel32.SetConsoleCursorPosition(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE());
+        
+        // Put the window to top-left.
+        var rect = GM.CreateVariable(8);
+        var srWindow = CONSOLE_SCREEN_BUFFER_INFO.Deref(10, 8).toBuffer();
+        rect.Deref(4, 2).toBuffer().writeUInt16LE(srWindow.readUInt16LE(4) - srWindow.readUInt16LE(0));
+        rect.Deref(6, 2).toBuffer().writeUInt16LE(srWindow.readUInt16LE(6) - srWindow.readUInt16LE(2));
+        
+        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')
+        {
+            return (require('fs').existsSync(process.env['windir'] + '\\SysWow64\\WindowsPowerShell\\v1.0\\powershell.exe'));
+        }
+        else
+        {
+            return (require('fs').existsSync(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'));
+        }
+    }
+
+    // Starts a Legacy Windows Terminal Session
+    this.StartEx = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, terminalTarget)
+    {
+        // The older windows terminal does not support 
+        CONSOLE_SCREEN_WIDTH = 80;
+        CONSOLE_SCREEN_HEIGHT = 25;
+
+        if (this._stream != null)
+        {
+            throw ('Concurrent terminal sessions are not supported on Windows.');
+        }
+        this.stopping = null;
+        if (this._kernel32.GetConsoleWindow().Val == 0) {
+            if (this._kernel32.AllocConsole().Val == 0) {
+                throw ('AllocConsole failed with: ' + this._kernel32.GetLastError().Val);
+            }
+        }
+        
+        this._stdinput = this._kernel32.GetStdHandle(STD_INPUT_HANDLE);
+        this._stdoutput = this._kernel32.GetStdHandle(STD_OUTPUT_HANDLE);
+        this._connected = false;
+
+        // 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);
+        
+        var rect = GM.CreateVariable(8);
+        rect.Deref(4, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH - 1);
+        rect.Deref(6, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT - 1);
+        
+        // 
+        // 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');
+        }
+
+        //
+        // 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');
+        }
+
+        // Hide the console window
+        this._user32.ShowWindow(this._kernel32.GetConsoleWindow().Val, SW_HIDE);
+
+        this.ClearScreen();
+        this._hookThread(terminalTarget).then(function ()
+        {
+            // Hook Ready
+            this.terminal.StartCommand(this.userArgs[0]);
+        }, console.log);
+        this._stream = new duplex(
+            {
+                'write': function (chunk, flush)
+                {
+                    if (!this.terminal.connected)
+                    {
+                        //console.log('_write: ' + chunk);
+                        if (!this._promise.chunk)
+                        {
+                            this._promise.chunk = [];
+                        }
+                        if (typeof (chunk) == 'string')
+                        {
+                            this._promise.chunk.push(chunk);
+                        } else
+                        {
+                            this._promise.chunk.push(Buffer.alloc(chunk.length));
+                            chunk.copy(this._promise.chunk.peek());
+                        }
+                        this._promise.chunk.peek().flush = flush;
+                        this._promise.then(function ()
+                        {
+                            var buf;
+                            while (this.chunk.length > 0)
+                            {
+                                buf = this.chunk.shift();
+                                this.terminal._WriteBuffer(buf);
+                                buf.flush();
+                            }
+                        });
+                    }
+                    else
+                    {
+                        //console.log('writeNOW: ' + chunk);
+                        this.terminal._WriteBuffer(chunk);
+                        flush();
+                    }
+                    return (true);
+                },
+                'final': function (flush)
+                {
+                    var p = this.terminal._stop();
+                    p.__flush = flush;
+                    p.then(function () { this.__flush(); });
+                }
+            });
+        this._stream.terminal = this;
+        this._stream._promise = new promise(function (res, rej) { this._res = res; this._rej = rej; });
+        this._stream._promise.terminal = this;
+        this._stream.prependOnceListener('end', function ()
+        {
+            this.terminal._stream = null;
+        });
+        return (this._stream);
+    };
+    this.Start = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT)
+    {
+        return (this.StartEx(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, process.env['windir'] + '\\System32\\cmd.exe'));
+    }
+    this.StartPowerShell = function StartPowerShell(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT)
+    {
+        if (require('os').arch() == 'x64')
+        {
+            if (require('fs').existsSync(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'))
+            {
+                return (this.StartEx(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'));
+            }
+            else
+            {
+                return (this.StartEx(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, process.env['windir'] + '\\SysWow64\\WindowsPowerShell\\v1.0\\powershell.exe'));
+            }
+        }
+        else
+        {
+            return (this.StartEx(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'));
+        }
+    }
+
+    this._stop = function () {
+        if (this.stopping) { return (this.stopping); }
+        //console.log('Stopping Terminal...');
+        this._ConsoleWinEventProc.removeAllListeners('GlobalCallback');
+        this.stopping = new promise(function (res, rej) { this._res = res; this._rej = rej; });
+        
+        var threadID = this._kernel32.GetThreadId(this._user32.SetWinEventHook.async.thread()).Val;
+        this._user32.PostThreadMessageA(threadID, WM_QUIT, 0, 0);
+        this._stream.emit('end');
+        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)
+        {
+            ret.userArgs.push(arguments[a]);
+        }
+        ret.terminal = this;
+        this._ConsoleWinEventProc = GM.GetGenericGlobalCallback(7);
+        this._ConsoleWinEventProc.terminal = this;
+        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)
+            {
+                this.ready._rej('Error calling SetWinEventHook');
+            } else
+            {
+                this.terminal.hwinEventHook = hwinEventHook;
+                this.ready._res();
+                this.terminal._GetMessage();
+            }
+        });
+
+        //
+        // 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;
+            
+            //
+            // 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();
+                    }
+                    if (this.terminal._scrollTimer == null) {
+                        buffer = this.terminal._GetScreenBuffer(LOWORD(idObject.Val), HIWORD(idObject.Val), LOWORD(idChild.Val), HIWORD(idChild.Val));
+                        //console.log('UPDATE REGION: [Left: ' + LOWORD(idObject.Val) + ' Top: ' +  HIWORD(idObject.Val) + ' Right: ' + LOWORD(idChild.Val) + ' Bottom: ' + HIWORD(idChild.Val) + ']');
+                        this.terminal._SendDataBuffer(buffer);
+                    }
+                    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 + ']');
+                        this.terminal._hProcess = null;
+                        this.terminal._stop().then(function () { console.log('STOPPED'); });
+                    }
+                    break;
+                default:
+                    //snprintf(Buf, 512, "unknown console event.\r\n");
+                    console.log('Unknown event: ' + dwEvent.Val);
+                    break;
+            }
+
+            //mbstowcs_s(&l, wBuf, Buf, 512);
+            //OutputDebugString(wBuf);
+
+        });
+        return (ret);
+    }
+    
+    // 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)
+        {
+            //console.log('GetMessage Response');
+            if (ret.Val != 0)
+            {
+                if (ret.Val == -1)
+                {
+                    // handle the error and possibly exit
+                }
+                else
+                {
+                    // Translates virtual-key messages into character messages
+                    //console.log('TranslateMessage');
+                    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.terminal._GetMessage();
+                        }, console.log);
+                    }, console.log);
+                }
+            } else
+            {
+                this.nativeProxy.UnhookWinEvent.async(this.nativeProxy.terminal._user32.SetWinEventHook.async, this.nativeProxy.terminal.hwinEventHook)
+                    .then(function ()
+                    {
+                        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)
+                        {
+                            var e = this.nativeProxy.terminal._kernel32.GetLastError().Val;
+                            console.log('Unable to kill Terminal Process, error: ' + e);
+                        }
+                        this.nativeProxy.terminal.stopping = null;
+                    }, function (err)
+                    {
+                        console.log('REJECTED_UnhookWinEvent: ' + 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._WriteCharacter(buf.charCodeAt(i), false);
+            } 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
+        rec.Deref(16, 4).toBuffer().writeUInt32LE(bControlKey);                             // rec.Event.KeyEvent.dwControlKeyState
+        rec.Deref(14, 1).toBuffer()[0] = key;                                               // rec.Event.KeyEvent.uChar.AsciiChar
+        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);
+    }
+    
+    // Get the current visible screen buffer
+    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)
+        {
+            // Use Default Parameters
+            sx = 0;
+            sy = 0;
+            ex = nWidth - 1;
+            ey = nHeight - 1;
+        } 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)
+        {
+            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)
+        {
+            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)
+            {
+                retVal.data.peek()[x] = line[x * 4];
+                retVal.attributes.peek()[x] = line[2 + (x * 4)];
+            }
+        }
+
+        return (retVal);
+    }
+    
+    this._SendDataBuffer = function (data)
+    {
+        // { data, attributes, width, height, x, y }
+        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)
+    {
+        //
+        // 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);
+        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;
+        
+        this._stream.push(GetEsc('H', [nHeight - 1, 0]));
+        for (var i = 0; i > nHeight; ++i) { this._stream.push(Buffer.from('\r\n')); }
+        
+        var buffer = this._GetScreenBuffer(0, 0, nWidth - 1, nHeight - 1);
+        this._SendDataBuffer(buffer);
+        
+        this._scrollTimer = setTimeout(function (self, nw, nh) {
+            var buffer = self._GetScreenBuffer(0, 0, nw - 1, nh - 1);
+            self._SendDataBuffer(buffer);
+            self._scrollTimer = null;
+        }, 250, this, nWidth, nHeight);
+    }
+    
+    this.StartCommand = function StartCommand(target) {
+        if (this._kernel32.CreateProcessA(GM.CreateVariable(target), 0, 0, 0, 1, CREATE_NEW_PROCESS_GROUP, 0, 0, si, pi).Val == 0)
+        {
+            console.log('Error Spawning CMD');
+            return;
+        }
+        
+        this._kernel32.CloseHandle(pi.Deref(GM.PointerSize, GM.PointerSize).Deref());           // pi.hThread
+        this._hProcess = pi.Deref(0, GM.PointerSize).Deref();                                   // pi.hProcess
+        this._hProcessID = pi.Deref(GM.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE(); // pi.dwProcessId
+        //console.log('Ready => hProcess: ' + this._hProcess._ptr + ' PID: ' + this._hProcessID);
+    }
+}
+
+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)
+{
+    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.
+
+    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;
+            bc = ((bc & 0x0001) << 2) + (bc & 0x0002) + ((bc & 0x0004) >> 2); // Background color
+            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 (esc.length > 0) { output.push(GetEsc('m', esc)); esc = []; }
+            lastAttr = attributes[i];
+        }
+        output.push(Buffer.from(String.fromCharCode(data[i])));
+    }
+
+    return Buffer.concat(output);
+}
+
+module.exports = new windows_terminal();

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно