diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3089701 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + }, + "python.formatting.provider": "none" +} \ No newline at end of file diff --git a/AutomateDiagnostics.py b/AutomateDiagnostics.py index 3aad0cf..0d0ffaa 100644 --- a/AutomateDiagnostics.py +++ b/AutomateDiagnostics.py @@ -6,46 +6,73 @@ import getopt import sys import cgi +import html #Added to fix escape error in Python3 def system_call(command): p = subprocess.Popen([command], stdout=subprocess.PIPE, shell=True) return p.stdout.read() -# Read state file for info -with open("/usr/local/ltechagent/state","r") as read_file: - data = json.load(read_file) +if os.path.exists("/usr/local/ltechagent/state"): -lterrors = "" -try: - opts, args = getopt.getopt(sys.argv[1:],"e") - for opt, arg in opts: - if opt == '-e': - with open("/usr/local/ltechagent/agent.log","r") as log_file: - lterrors = cgi.escape(log_file.read()) -except getopt.GetoptError: - pass + # Read state file for info + with open("/usr/local/ltechagent/state","r") as read_file: + data = json.load(read_file) + # Get last contact date + lc = data["last_contact"] + last_contact = "{0}/{1}/{2} {3}:{4:02d}:{5:02d}".format(lc["month"],lc["day_of_month"],lc["year"],lc["hour"],lc["min"],lc["sec"]) -lterrors_bytes = lterrors.encode("ascii") -lterrors_b64_bytes = base64.b64encode(lterrors_bytes) -lterrors_str = lterrors_b64_bytes.decode("ascii") + old_version = data["version"] -# Get last contact date -lc = data["last_contact"] -last_contact = "{0}/{1}/{2} {3}:{4:02d}:{5:02d}".format(lc["month"],lc["day_of_month"],lc["year"],lc["hour"],lc["min"],lc["sec"]) + system_call("/usr/local/ltechagent/ltupdate") -old_version = data["version"] + # Read state file for info + with open("/usr/local/ltechagent/state","r") as read_file: + data = json.load(read_file) -system_call("/usr/local/ltechagent/ltupdate") + if old_version != data["version"]: + update = "Updated from "+old_version+" to "+ data["version"] + else: + update = "Already updated to "+ data["version"] + + server_addr = data["last_good_server_url"] + version = data["version"] + id = data['computer_id'] + clientid = data['client_id'] + online = data["is_signed_in"] + +else: + + server_addr = 'Not Found' + last_contact = '' + version = '' + update = '' + id = '' + clientid = '' + online = '' -# Read state file for info -with open("/usr/local/ltechagent/state","r") as read_file: - data = json.load(read_file) +if os.path.exists("/usr/local/ltechagent/agent.log"): + + lterrors = "" + + try: + opts, args = getopt.getopt(sys.argv[1:],"e") + for opt, arg in opts: + if opt == '-e': + with open("/usr/local/ltechagent/agent.log","r") as log_file: + lterrors = html.escape(log_file.read()) + #Changed from cgi.escape to html.escape to resolve errors in Python3 + + except getopt.GetoptError: + pass + + lterrors_bytes = lterrors.encode("ascii") + lterrors_b64_bytes = base64.b64encode(lterrors_bytes) + lterrors_str = lterrors_b64_bytes.decode("ascii") -if old_version != data["version"]: - update = "Updated from "+old_version+" to "+ data["version"] else: - update = "Already updated to "+ data["version"] + + lterrors_str = "Log file not found" # Check services if platform.system() == 'Darwin': @@ -75,17 +102,17 @@ def system_call(command): statusname = "Stopped" svc_ltsvc = { "Status": statusname, "User": "ltechagent", "Start Mode": "Auto"} -diag_result = { - 'server_addr': data["last_good_server_url"], +diag_result = { + 'server_addr': server_addr, 'lastcontact': last_contact, 'update': update, - 'version': data["version"], - 'id': data['computer_id'], - 'clientid': data['client_id'], - 'online': data["is_signed_in"], + 'version': version, + 'id': id, + 'clientid': clientid, + 'online': online, 'svc_ltservice': svc_ltsvc, 'lterrors': lterrors_str } print("!---BEGIN JSON---!") -print(json.dumps(diag_result)) +print(json.dumps(diag_result)) \ No newline at end of file diff --git a/DiagnosticsNewStyle.css b/DiagnosticsNewStyle.css index d03a9cc..462e115 100644 --- a/DiagnosticsNewStyle.css +++ b/DiagnosticsNewStyle.css @@ -8,6 +8,7 @@ margin-left: auto; margin-right: auto; table-layout: fixed; + margin: 0.4em 0em; } .Automate .Header { @@ -19,6 +20,12 @@ display: none; } +/* 2023.10.17 -- Joe McCall | So the Diagnostic Action in the context menu aligns correctly. */ + +.GetInfoPer { + text-align: left; +} + .success, .failed { font-weight: bold !important; @@ -44,19 +51,6 @@ z-index: 5; } -/*a.GetInfo.Automate.HasText { - display: inline-block; - width: 13em; - text-decoration: inherit; - color: #FFF !important; - border-radius: 0.2em; - padding: 0.4em 0.5em; - font-size: 1.0em; - font-weight: 500; - text-align: left; - background-color: #026CCF; -}*/ - #dataTableHeader, #dataTableData { border: 0px; @@ -76,7 +70,6 @@ } #dataTable { - top: 10%; width: 95%; } @@ -106,7 +99,6 @@ */ font-size: 0.9em; background-color: #252525; - color: #FFFFFF; overflow: auto; padding: 0.5em 0em 0em 0.5em; height: 36em; @@ -145,15 +137,13 @@ } /* 2023.01.16 -- Joe McCall | Quality of life. */ - .Automate a:hover { background-color: #014e95 !important; } /* 2023.01.16 -- Joe McCall | Reduce wasted space */ - -#tableDetails, #dataContainer { + width: 100%; margin: 0em 0em 0.2em 0em !important; } @@ -174,7 +164,6 @@ .Automate hr, .Automate dt, .Automate dd, -.Automate table .Automate pre { - margin: 0.2em 0em; + margin: 0.4em 0em; } \ No newline at end of file diff --git a/Initializer.js b/Initializer.js index f593abf..a969568 100644 --- a/Initializer.js +++ b/Initializer.js @@ -1,297 +1,328 @@ SC.event.addGlobalHandler(SC.event.PreRender, function (eventArgs) { - if (SC.context.pageType == "HostPage") { + if (SC.context.pageType == 'HostPage') { SC.util.includeStyleSheet( - extensionContext.baseUrl + "DiagnosticsNewStyle.css" - ); + extensionContext.baseUrl + 'DiagnosticsNewStyle.css' + ) } -}); +}) -function getLTPoSh() { - return extensionContext.settingValues.PathToLTPoSh; +function getLTPoSh () { + return extensionContext.settingValues.PathToLTPoSh } -function getAutomateDiagnosticsURL() { - return extensionContext.settingValues.PathToDiag; +function getAutomateDiagnosticsURL () { + return extensionContext.settingValues.PathToDiag } -function getLinuxDiagnosticsURL() { - return extensionContext.settingValues.PathToLinuxDiag; +function getLinuxDiagnosticsURL () { + return extensionContext.settingValues.PathToLinuxDiag } -function getMacDiagnosticsURL() { - return extensionContext.settingValues.PathToMacDiag; +function getMacDiagnosticsURL () { + return extensionContext.settingValues.PathToMacDiag } -function getLTServer() { - return extensionContext.settingValues.AutomateHostname; +function getLTServer () { + return extensionContext.settingValues.AutomateHostname } -function getInstallerToken() { - return extensionContext.settingValues.InstallerToken; +function getInstallerToken (osName) { + if (osName.match('Windows')) { + return extensionContext.settingValues.InstallerToken + } else if (osName.match('Mac OS')) { + return extensionContext.settingValues.InstallerTokenMac + } else { + return extensionContext.settingValues.InstallerTokenLinux + } } -function getAgentVersionProp() { - return extensionContext.settingValues.AgentVersionCustomProperty; +function getAgentVersionProp () { + return extensionContext.settingValues.AgentVersionCustomProperty } -function getTimeout() { - return extensionContext.settingValues.Timeout; +function getTimeout () { + return extensionContext.settingValues.Timeout } -function getVerbose() { - if (extensionContext.settingValues.Verbose == "1") { - return "-Verbose"; +function getVerbose () { + if (extensionContext.settingValues.Verbose == '1') { + return '-Verbose' } else { - return ""; + return '' } } SC.event.addGlobalHandler(SC.event.QueryCommandButtons, function (eventArgs) { switch (eventArgs.area) { - case "HostDetailTabList": + case 'HostDetailTabList': eventArgs.buttonDefinitions.push({ - commandName: "Select", - commandArgument: "Automate", - text: SC.res["Diagnostics.Automate.Label"], - imageUrl: extensionContext.baseUrl + "Automate.png", - }); - break; - case "HostDetailPopoutPanel": + commandName: 'Select', + commandArgument: 'Automate', + text: SC.res['Diagnostics.Automate.Label'], + imageUrl: extensionContext.baseUrl + 'Automate.png' + }) + break + case 'HostDetailPopoutPanel': eventArgs.buttonDefinitions.push({ - commandName: "GetInfoPer", - commandArgument: "Automate", - text: SC.res["Diagnostics.Automate.Button"], - }); - break; - case "AutomateButtons": + commandName: 'GetInfoPer', + commandArgument: 'Automate', + text: SC.res['Diagnostics.Automate.Button'] + }) + break + case 'AutomateButtons': eventArgs.buttonDefinitions.push({ - commandName: "GetInfo", - commandArgument: "Automate", - text: SC.res["Diagnostics.Automate.Button"], - }); - break; - case "ReinstallButton": + commandName: 'GetInfo', + commandArgument: 'Automate', + text: SC.res['Diagnostics.Automate.Button'] + }) + break + case 'ReinstallButton': eventArgs.buttonDefinitions.push({ - commandName: "ReinstallAutomate", - commandArgument: "Automate", - text: SC.res["Diagnostics.Automate.ReinstallButton"], - }); - break; - case "RestartButton": + commandName: 'ReinstallAutomate', + commandArgument: 'Automate', + text: SC.res['Diagnostics.Automate.ReinstallButton'] + }) + break + case 'RestartButton': eventArgs.buttonDefinitions.push({ - commandName: "RestartAutomate", - commandArgument: "Automate", - text: SC.res["Diagnostics.Automate.RestartButton"], - }); - break; + commandName: 'RestartAutomate', + commandArgument: 'Automate', + text: SC.res['Diagnostics.Automate.RestartButton'] + }) + break } -}); +}) SC.event.addGlobalHandler(SC.event.InitializeTab, function (eventArgs) { if (isMyTab(eventArgs.tabName)) { - SC.ui.addElement(eventArgs.container, "DIV", { id: "diagTopContainer" }); - var diagButton = SC.ui.addElement($("diagTopContainer"), "div", { - id: "diagButtonContainer", - className: "DiagActions", - }); + //2023.01.18 -- Joe McCall | Adjusted the HTML structure to make use of the CollapsiblePanel classes for formatting only. + SC.ui.addElement(eventArgs.container, 'DIV', { id: 'diagTopContainer' }) + var diagButton = SC.ui.addElement($('diagTopContainer'), 'div', { + id: 'diagButtonContainer', + className: 'DiagActions' + }) SC.command.queryAndAddCommandButtons( diagButton, - eventArgs.tabName + "Buttons" - ); - SC.ui.addElement($("diagTopContainer"), "DIV", { - id: "lastUpdateContainer", - }); - SC.ui.addElement(eventArgs.container, "DIV", { - id: "detailsPanel", - className: "CollapsiblePanel", - }); - SC.ui.addElement($("detailsPanel"), "DIV", { - id: "dataContainer", - className: "Header", - }); - SC.ui.addElement($("detailsPanel"), "TABLE", { id: "dataTable" }); - SC.ui.addElement(eventArgs.container, "DIV", { - id: "repairOptions", - className: "CollapsiblePanel", - }); - SC.ui.addElement(eventArgs.container, "DIV", { - id: "lterrors", - className: "CollapsiblePanel", - }); + eventArgs.tabName + 'Buttons' + ) + SC.ui.addElement($('diagTopContainer'), 'DIV', { + id: 'lastUpdateContainer' + }) + SC.ui.addElement(eventArgs.container, 'DIV', { + id: 'detailsPanel', + className: 'CollapsiblePanel' + }) + SC.ui.addElement($('detailsPanel'), 'DIV', { + id: 'dataContainer', + className: 'Header' + }) + SC.ui.addElement($('detailsPanel'), 'TABLE', { id: 'dataTable' }) + SC.ui.addElement(eventArgs.container, 'DIV', { + id: 'repairOptions', + className: 'CollapsiblePanel' + }) + SC.ui.addElement(eventArgs.container, 'DIV', { + id: 'lterrors', + className: 'CollapsiblePanel' + }) } -}); +}) SC.event.addGlobalHandler(SC.event.RefreshTab, function (eventArgs) { if (isMyTab(eventArgs.tabName)) { - SC.ui.clear($("dataContainer")); - SC.ui.clear($("lastUpdateContainer")); - SC.ui.clear($("dataTable")); - SC.ui.clear($("repairOptions")); - SC.ui.clear($("lterrors")); + SC.ui.clear($('dataContainer')) + SC.ui.clear($('lastUpdateContainer')) + SC.ui.clear($('dataTable')) + SC.ui.clear($('repairOptions')) + SC.ui.clear($('lterrors')) SC.ui.findDescendent(eventArgs.container, function (e) { - return e._commandName == "GetInfo"; + return e._commandName == 'GetInfo' })._commandArgument = { type: eventArgs.tabName, - operatingSystemName: eventArgs.session.GuestOperatingSystemName, - }; + operatingSystemName: eventArgs.session.GuestOperatingSystemName + } displayAutomateDiagInfo( getLatestDiagnosticEvent(eventArgs.sessionDetails, eventArgs.tabName), eventArgs.sessionDetails.BaseTime - ); + ) + + try { + SC.ui.findDescendent(eventArgs.container, function (e) { + return e._commandName == 'RestartAutomate' + })._commandArgument = { + type: 'RestartAutomate', + operatingSystemName: eventArgs.session.GuestOperatingSystemName + } + } catch {} + + try { + SC.ui.findDescendent(eventArgs.container, function (e) { + return e._commandName == 'ReinstallAutomate' + })._commandArgument = { + type: 'ReinstallAutomate', + operatingSystemName: eventArgs.session.GuestOperatingSystemName + } + } catch {} } -}); +}) SC.event.addGlobalHandler(SC.event.ExecuteCommand, function (eventArgs) { + console.log(eventArgs) switch (eventArgs.commandName) { - case "GetInfo": - sendCommand(eventArgs); - break; - case "ReinstallAutomate": - showReinstallPrompt(); - break; - case "RestartAutomate": + case 'GetInfo': + sendCommand(eventArgs) + break + case 'ReinstallAutomate': + //sendCommand(eventArgs); + showReinstallPrompt(eventArgs.commandArgument.operatingSystemName) + break + case 'RestartAutomate': sendCommand({ commandArgument: { - type: "RestartAutomate", - operatingSystemName: "Windows", - }, - }); - break; - case "GetInfoPer": + type: 'RestartAutomate', + operatingSystemName: eventArgs.commandArgument.operatingSystemName + } + }) + break + case 'GetInfoPer': var checkedOrSelectedRows = Array.prototype.filter.call( - ($("detailTable") || $(".DetailTable")).rows, + ($('detailTable') || $('.DetailTable')).rows, function (r) { - return SC.ui.isChecked(r) || SC.ui.isSelected(r); + return SC.ui.isChecked(r) || SC.ui.isSelected(r) } - ); + ) var checkedOrSelectedSessions = Array.prototype.map.call( checkedOrSelectedRows, function (r) { - return r._dataItem; + return r._dataItem } - ); + ) var sessionType = checkedOrSelectedSessions[0].SessionType === undefined ? SC.types.SessionTypes.Access - : checkedOrSelectedSessions[0].SessionType; + : checkedOrSelectedSessions[0].SessionType var windowsSessionIDs = Array.prototype.map .call(checkedOrSelectedSessions, function (s) { - if (s.GuestOperatingSystemName.includes("Windows")) - return s.SessionID; + if (s.GuestOperatingSystemName.includes('Windows')) return s.SessionID }) .filter(function (s) { - return s !== undefined; - }); + return s !== undefined + }) var linuxMacSessionIDs = Array.prototype.map .call(checkedOrSelectedSessions, function (s) { - if (!s.GuestOperatingSystemName.includes("Windows")) - return s.SessionID; + if (!s.GuestOperatingSystemName.includes('Windows')) + return s.SessionID }) .filter(function (s) { - return s !== undefined; - }); + return s !== undefined + }) window.addEventToSessions( window.getSessionGroupUrlPart()[0], SC.types.SessionType.Access, windowsSessionIDs, SC.types.SessionEventType.QueuedCommand, null, - getAutomateInputCommand("Automate", "Windows"), + getAutomateInputCommand('Automate', 'Windows'), false, false, true - ); + ) window.addEventToSessions( window.getSessionGroupUrlPart()[0], SC.types.SessionType.Access, linuxMacSessionIDs, SC.types.SessionEventType.QueuedCommand, null, - getAutomateInputCommand("Automate", "Linux"), + getAutomateInputCommand('Automate', 'Linux'), false, false, true - ); - break; + ) + break } -}); +}) SC.event.addGlobalHandler(SC.event.PreRender, function (eventArgs) { - if (typeof extensionContext !== "undefined") { + if (typeof extensionContext !== 'undefined') { if (extensionContext.settingValues.CreateVersionSessionGroup == 1) { - var versionProperty = getAgentVersionProp(); - SC.service.NotifyCreatedVersionSessionGroup(); - SC.service.SetVersionCustomProperties(); + var versionProperty = getAgentVersionProp() + SC.service.NotifyCreatedVersionSessionGroup() + SC.service.SetVersionCustomProperties() SC.service.GetSessionGroups(function (sessionGroups) { for ( - var sessionTypesAsString = ["Sessions", "Meetings", "Machines"], + var sessionTypesAsString = ['Sessions', 'Meetings', 'Machines'], sessionType = 0; sessionType < sessionTypesAsString.length; sessionType++ ) { var name = - "All " + sessionTypesAsString[sessionType] + " by CWA Version"; + 'All ' + sessionTypesAsString[sessionType] + ' by CWA Version' if ( !sessionGroups.find(function (session) { - return session.Name === name; + return session.Name === name }) ) { sessionGroups.push({ Name: name, - SessionFilter: "NOT CustomProperty" + versionProperty + " = ''", + SessionFilter: 'NOT CustomProperty' + versionProperty + " = ''", SessionType: sessionType, - SubgroupExpressions: "CustomProperty" + versionProperty, - }); + SubgroupExpressions: 'CustomProperty' + versionProperty + }) } } - SC.service.SaveSessionGroups(sessionGroups); - }); + SC.service.SaveSessionGroups(sessionGroups) + }) } } -}); +}) -function showReinstallPrompt() { - var locationId = $("#locationidval").innerHTML; - var installertoken = getInstallerToken(); +function showReinstallPrompt (osName) { + //console.log(osName); + try { + //2024.08.05 -- Joe McCall | As of at least 24.1.9, innerHTML started returning the raw element; this gets the text content then drops the checkmark/x and space before the ID. + var locationId = $('#locationid').innerText.substring(2); + } catch {} + var installertoken = getInstallerToken(osName) SC.dialog.showModalDialogRaw( - "JoinSessionWithOptions", + 'JoinSessionWithOptions', [ - SC.dialog.createTitlePanel("Reinstall Automate Agent"), + SC.dialog.createTitlePanel('Reinstall Automate Agent'), SC.dialog.createContentPanel([ $dl( [ - $dt("Location ID"), + $dt('Location ID'), $dd( (txtlocationid = $input({ - id: "locationidreinstall", - type: "text", - value: locationId ? locationId : 1, + id: 'locationidreinstall', + type: 'text', + value: locationId ? locationId : 1 })) - ), + ) ], [ - $dt("Installer Token"), + $dt('Installer Token'), $dd( (txtlocationid = $input({ - id: "installertoken", - type: "text", - value: installertoken, + id: 'installertoken', + type: 'text', + value: installertoken })) - ), + ) ] - ), + ) ]), - (buttonPanel = SC.dialog.createButtonPanel("Reinstall")), + (buttonPanel = SC.dialog.createButtonPanel('Reinstall')) ], function (eventArgs, dialog) { - SC.dialog.hideModalDialog(); + SC.dialog.hideModalDialog() sendCommand({ commandArgument: { - type: "ReinstallAutomate", - operatingSystemName: "Windows", - }, - }); + type: 'ReinstallAutomate', + operatingSystemName: osName + } + }) } - ); + ) } -function sendCommand(eventArgs) { +function sendCommand (eventArgs) { + //console.log(eventArgs); window.addEventToSessions( window.getSessionGroupUrlPart()[0], SC.types.SessionType.Access, @@ -305,470 +336,466 @@ function sendCommand(eventArgs) { false, false, true - ); + ) } -function getAutomateInputCommand(diagnosticType, operatingSystem) { - var headers = getHeaders(operatingSystem); - headers.DiagnosticType = diagnosticType; - var commandText = getAutomateCommandText(headers); - var timeout = getTimeout(); - var emptyLinePrefix = ""; +function getAutomateInputCommand (diagnosticType, operatingSystem) { + //console.log(diagnosticType); + //console.log(operatingSystem); + + var headers = getHeaders(operatingSystem) + headers.DiagnosticType = diagnosticType + var commandText = getAutomateCommandText(headers) + var timeout = getTimeout() + var emptyLinePrefix = '' - if (headers.Processor == "sh") emptyLinePrefix = "echo "; + if (headers.Processor == 'sh') emptyLinePrefix = 'echo ' else { - emptyLinePrefix = 'echo ""'; + emptyLinePrefix = 'echo ""' } - + //console.log(headers); return ( - "#!" + + '#!' + headers.shaBang + - "\n" + - "#maxlength=10000000" + - "\n" + - "#timeout=" + + '\n' + + '#maxlength=10000000' + + '\n' + + '#timeout=' + timeout + - "\n" + + '\n' + headers.modifier + - "DIAGNOSTIC-RESPONSE/1" + + 'DIAGNOSTIC-RESPONSE/1' + headers.delimiter + - "\n" + + '\n' + headers.modifier + - "DiagnosticType: " + + 'DiagnosticType: ' + headers.DiagnosticType + headers.delimiter + - "\n" + + '\n' + headers.modifier + - "ContentType: " + + 'ContentType: ' + headers.ContentType + headers.delimiter + - "\n" + + '\n' + emptyLinePrefix + - "\n" + + '\n' + commandText - ); + ) } -function getHeaders(operatingSystem) { - //2023.01.16 -- Joe McCall | Windows matching was not working reliably, because Windows OS does not start with Windows on newer OS (Microsoft Windows 1x) - if (operatingSystem.match("Windows")) { +function getHeaders (operatingSystem) { + if (operatingSystem.match('Windows')) { return { - Processor: "ps", - Interface: "powershell", - ContentType: "json", - shaBang: "ps", + Processor: 'ps', + Interface: 'powershell', + ContentType: 'json', + shaBang: 'ps', modifier: 'echo "', delimiter: '"', - OperatingSystem: "Windows", - }; - } - //2023.01.16 -- Joe McCall | Split Mac OS and Linux - else if (operatingSystem.startsWith("Mac OS")) { + OperatingSystem: 'Windows' + } + } else if (operatingSystem.startsWith('Mac OS')) { return { - Processor: "sh", - Interface: "bash", - ContentType: "json", - shaBang: "sh", - modifier: "echo ", - delimiter: "", - OperatingSystem: "Mac", - }; + Processor: 'sh', + Interface: 'bash', + ContentType: 'json', + shaBang: 'sh', + modifier: 'echo ', + delimiter: '', + OperatingSystem: 'Mac' + } } else { return { - Processor: "sh", - Interface: "bash", - ContentType: "json", - shaBang: "sh", - modifier: "echo ", - delimiter: "", - OperatingSystem: "Linux", - }; + Processor: 'sh', + Interface: 'bash', + ContentType: 'json', + shaBang: 'sh', + modifier: 'echo ', + delimiter: '', + OperatingSystem: 'Linux' + } } } -function isMyTab(tabName) { +function isMyTab (tabName) { switch (tabName) { - case "Automate": - return true; + case 'Automate': + return true default: - return false; + return false } } -function isDiagnosticContent(eventData) { - return eventData.startsWith("DIAGNOSTIC-RESPONSE/1") || - (eventData.startsWith("\ufeffDIAGNOSTIC-RESPONSE/1") && - eventData.match("/!---BEGIN JSON---!/g")) +function isDiagnosticContent (eventData) { + return eventData.startsWith('DIAGNOSTIC-RESPONSE/1') || + (eventData.startsWith('\ufeffDIAGNOSTIC-RESPONSE/1') && + eventData.match('/!---BEGIN JSON---!/g')) ? true - : false; + : false } -function getLatestDiagnosticEvent(sessionDetails, diagnosticEventType) { - // 2023.01.16 -- Joe McCall | Parsing of sessionDetails.Connections fails; sessionDetails.Events requires no additional parsing. +function getLatestDiagnosticEvent (sessionDetails, diagnosticEventType) { return sessionDetails.Events.filter(function (e) { return ( e.EventType === SC.types.SessionEventType.RanCommand && isDiagnosticContent(e.Data) && parseDataHeaders(e.Data).DiagnosticType.trim() == diagnosticEventType - ); + ) }).sort(function (x, y) { - return x.Time - y.Time; - })[0]; + return x.Time - y.Time + })[0] } -function parseDataHeaders(eventData) { - var currentIndex = 0; - var headers = {}; - var isStatusLine = true; +function parseDataHeaders (eventData) { + var currentIndex = 0 + var headers = {} + var isStatusLine = true while (true) { - var nextNewLineIndex = eventData.indexOf("\n", currentIndex); + var nextNewLineIndex = eventData.indexOf('\n', currentIndex) if (isStatusLine) { - isStatusLine = false; + isStatusLine = false } else if (nextNewLineIndex == currentIndex + 2 || nextNewLineIndex < 0) { - break; + break } else { var lineParts = eventData .substring(currentIndex, nextNewLineIndex) - .split(": "); - headers[lineParts[0]] = lineParts[1]; + .split(': ') + headers[lineParts[0]] = lineParts[1] } - currentIndex = nextNewLineIndex + 1; + currentIndex = nextNewLineIndex + 1 } - return headers; + return headers } -function displayAutomateDiagInfo(latestDiagnosticEvent, baseTime) { +function displayAutomateDiagInfo (latestDiagnosticEvent, baseTime) { try { - var headers = parseDataHeaders(latestDiagnosticEvent.Data); - var output = latestDiagnosticEvent.Data; - var data = output.split("!---BEGIN JSON---!"); - //console.log(data[1]); - displayDataJson(parseJson(data[1])); - $("lastUpdateContainer").innerHTML = - SC.res["Diagnostics.LastUpdateField.Label"] + - new Date(latestDiagnosticEvent.Time + baseTime).toLocaleString(); + var headers = parseDataHeaders(latestDiagnosticEvent.Data) + var output = latestDiagnosticEvent.Data + var data = output.split('!---BEGIN JSON---!') + + displayDataJson(parseJson(data[1])) + //2023.01.19 -- Joe McCall | Time calculation was inverted and showing a future time. + $('lastUpdateContainer').innerHTML = + SC.res['Diagnostics.LastUpdateField.Label'] + + new Date(baseTime - latestDiagnosticEvent.Time).toLocaleString() } catch (e) { - console.log("No diagnostic data to display"); - // 2023.01.16 -- Joe McCall | Uncomment for additional debugging. - // console.log(e.message); + console.log('No diagnostic data to display') } } -function extractJSON(str) { - var firstOpen, firstClose, candidate; - firstOpen = str.indexOf("{", firstOpen + 1); +function extractJSON (str) { + var firstOpen, firstClose, candidate + firstOpen = str.indexOf('{', firstOpen + 1) do { - firstClose = str.lastIndexOf("}"); + firstClose = str.lastIndexOf('}') if (firstClose <= firstOpen) { - return null; + return null } do { - candidate = str.substring(firstOpen, firstClose + 1); + candidate = str.substring(firstOpen, firstClose + 1) try { - var res = JSON.parse(candidate); + var res = JSON.parse(candidate) //console.log('...found'); - return res; + return res } catch (e) {} - firstClose = str.substr(0, firstClose).lastIndexOf("}"); - } while (firstClose > firstOpen); - firstOpen = str.indexOf("{", firstOpen + 1); - } while (firstOpen != -1); + firstClose = str.substr(0, firstClose).lastIndexOf('}') + } while (firstClose > firstOpen) + firstOpen = str.indexOf('{', firstOpen + 1) + } while (firstOpen != -1) } -function parseJson(eventData) { - var json = extractJSON(eventData); - //console.log(json); - return json; +function parseJson (eventData) { + var json = extractJSON(eventData) + return json } -function displayDataJson(json) { - SC.ui.addElement($("dataContainer"), "h3", { - id: "tableDetails", - innerHTML: "Details", - }); - - console.log(json); +function displayDataJson (json) { + //2023.01.19 -- Joe McCall | Re-ordered the Details elements and added a more uniform formatting for all entries. + SC.ui.addElement($('dataContainer'), 'h3', { + id: 'tableDetails', + innerHTML: 'Details' + }) - if ("server_addr" in json) { - if (!/Error/i.test(json["server_addr"]) && json["server_addr"] != null) { - var server_status = ""; + if ('server_addr' in json) { + if (!/Error/i.test(json['server_addr']) && json['server_addr'] != null) { + var server_status = "" } else { - var server_status = ""; + var server_status = "" } - if (json["server_addr"] != null) { - server = json["server_addr"]; + if (json['server_addr'] != null) { + server = json['server_addr'] } else { - server = "No Agent Installed"; + server = 'No Agent Installed' } - SC.ui.addElement($("dataTable"), "tr", { id: "server_row" }); - SC.ui.addElement($("server_row"), "th", { - id: "server_hdr", - innerHTML: "Server Check", - }); - SC.ui.addElement($("server_row"), "td", { - id: "server", - innerHTML: server_status + " " + server, - colspan: 2, - }); + SC.ui.addElement($('dataTable'), 'tr', { id: 'server_row' }) + SC.ui.addElement($('server_row'), 'th', { + id: 'server_hdr', + innerHTML: 'Server Check' + }) + SC.ui.addElement($('server_row'), 'td', { + id: 'server', + innerHTML: server_status + ' ' + server, + colspan: 2 + }) } - if ("id" in json) { - if (json["id"] > 0) { - var agentid_status = ""; + if ('id' in json) { + if (json['id'] > 0) { + var agentid_status = "" } else { - var agentid_status = ""; + var agentid_status = "" } - SC.ui.addElement($("dataTable"), "tr", { id: "agent_id_row" }); - SC.ui.addElement($("agent_id_row"), "th", { - id: "agent_id_hdr", - innerHTML: "Agent ID", - }); - SC.ui.addElement($("agent_id_row"), "td", { - id: "agent_id", - innerHTML: agentid_status + " " + json["id"], - }); + SC.ui.addElement($('dataTable'), 'tr', { id: 'agent_id_row' }) + SC.ui.addElement($('agent_id_row'), 'th', { + id: 'agent_id_hdr', + innerHTML: 'Agent ID' + }) + SC.ui.addElement($('agent_id_row'), 'td', { + id: 'agent_id', + innerHTML: agentid_status + ' ' + json['id'] + }) } - if ("locationid" in json) { - if (json["locationid"] > 0) { - var locationid_status = ""; + if ('locationid' in json) { + if (json['locationid'] > 0) { + var locationid_status = "" } else { - var locationid_status = ""; + var locationid_status = "" } - SC.ui.addElement($("dataTable"), "tr", { id: "locationid_row" }); - SC.ui.addElement($("locationid_row"), "th", { - id: "locationid_hdr", - innerHTML: "Location ID", - }); - SC.ui.addElement($("locationid_row"), "td", { - id: "locationid", - innerHTML: - locationid_status + - " " + - json["locationid"] + - "", - }); + SC.ui.addElement($('dataTable'), 'tr', { id: 'locationid_row' }) + SC.ui.addElement($('locationid_row'), 'th', { + id: 'locationid_hdr', + innerHTML: 'Location ID' + }) + SC.ui.addElement($('locationid_row'), 'td', { + id: 'locationid', + innerHTML: locationid_status + ' ' + json['locationid'] + }) } - if ("update" in json) { - if (!/Error/i.test(json["update"])) { - var update_status = ""; + if ('update' in json) { + if (!/Error/i.test(json['update'])) { + var update_status = "" } else { - var update_status = ""; + var update_status = "" } - SC.ui.addElement($("dataTable"), "tr", { id: "update_row" }); - SC.ui.addElement($("update_row"), "th", { - id: "agent_id_hdr", - innerHTML: "Update Check", - }); - SC.ui.addElement($("update_row"), "td", { - id: "agent_id", - innerHTML: update_status + " " + json["update"], - colspan: 2, - }); + SC.ui.addElement($('dataTable'), 'tr', { id: 'update_row' }) + SC.ui.addElement($('update_row'), 'th', { + id: 'agent_id_hdr', + innerHTML: 'Update Check' + }) + SC.ui.addElement($('update_row'), 'td', { + id: 'agent_id', + innerHTML: update_status + ' ' + json['update'], + colspan: 2 + }) } - if ("online" in json) { - var online_status = json["online"] + if ('online' in json) { + var online_status = json['online'] ? "" - : ""; - SC.ui.addElement($("dataTable"), "tr", { id: "status_row" }); - SC.ui.addElement($("status_row"), "th", { - id: "status_hdr", - innerHTML: "Checkin Health", - }); - SC.ui.addElement($("status_row"), "td", { - id: "status", - innerHTML: online_status + " " + json["lastcontact"], - }); + : "" + SC.ui.addElement($('dataTable'), 'tr', { id: 'status_row' }) + SC.ui.addElement($('status_row'), 'th', { + id: 'status_hdr', + innerHTML: 'Checkin Health' + }) + SC.ui.addElement($('status_row'), 'td', { + id: 'status', + innerHTML: online_status + ' ' + json['lastcontact'] + }) } - if ("heartbeat" in json) { - var heartbeat_status = json["heartbeat"] + if ('heartbeat' in json) { + var heartbeat_status = json['heartbeat'] ? "" - : ""; - SC.ui.addElement($("dataTable"), "tr", { id: "status_row2" }); - SC.ui.addElement($("status_row2"), "th", { - id: "status_hdr2", - innerHTML: "Heartbeat Health", - }); - SC.ui.addElement($("status_row2"), "td", { - id: "status2", - innerHTML: heartbeat_status + " " + json["heartbeat_sent"], - }); + : "" + SC.ui.addElement($('dataTable'), 'tr', { id: 'status_row2' }) + SC.ui.addElement($('status_row2'), 'th', { + id: 'status_hdr2', + innerHTML: 'Heartbeat Health' + }) + SC.ui.addElement($('status_row2'), 'td', { + id: 'status2', + innerHTML: heartbeat_status + ' ' + json['heartbeat_sent'] + }) } - if ("svc_ltservice" in json) { - if (json["svc_ltservice"]["Status"] != "Not Detected") { + if ('svc_ltservice' in json) { + if (json['svc_ltservice']['Status'] != 'Not Detected') { var ltservice_txt = - json["svc_ltservice"]["Status"] + - " | " + - json["svc_ltservice"]["Start Mode"] + - " | " + - json["svc_ltservice"]["User"]; + json['svc_ltservice']['Status'] + + ' | ' + + json['svc_ltservice']['Start Mode'] + + ' | ' + + json['svc_ltservice']['User'] } else { - var ltservice_txt = json["svc_ltservice"]["Status"]; + var ltservice_txt = json['svc_ltservice']['Status'] } if ( - json["svc_ltservice"]["Status"] == "Running" && - json["svc_ltservice"]["Start Mode"] == "Auto" + json['svc_ltservice']['Status'] == 'Running' && + json['svc_ltservice']['Start Mode'] == 'Auto' ) { - var ltservice_status = ""; + var ltservice_status = "" } else { - var ltservice_status = ""; + var ltservice_status = "" } - SC.ui.addElement($("dataTable"), "tr", { id: "ltsvc_row" }); - SC.ui.addElement($("ltsvc_row"), "th", { - id: "agent_id_hdr", - innerHTML: "SVC - LTService", - }); - SC.ui.addElement($("ltsvc_row"), "td", { - id: "ltsvc", - innerHTML: ltservice_status + " " + ltservice_txt, - }); + SC.ui.addElement($('dataTable'), 'tr', { id: 'ltsvc_row' }) + SC.ui.addElement($('ltsvc_row'), 'th', { + id: 'agent_id_hdr', + innerHTML: 'SVC - LTService' + }) + SC.ui.addElement($('ltsvc_row'), 'td', { + id: 'ltsvc', + innerHTML: ltservice_status + ' ' + ltservice_txt + }) } - if ("svc_ltsvcmon" in json) { - if (json["svc_ltsvcmon"]["Status"] != "Not Detected") { + if ('svc_ltsvcmon' in json) { + if (json['svc_ltsvcmon']['Status'] != 'Not Detected') { var ltsvcmon_txt = - json["svc_ltsvcmon"]["Status"] + - " | " + - json["svc_ltsvcmon"]["Start Mode"] + - " | " + - json["svc_ltsvcmon"]["User"]; + json['svc_ltsvcmon']['Status'] + + ' | ' + + json['svc_ltsvcmon']['Start Mode'] + + ' | ' + + json['svc_ltsvcmon']['User'] } else { - var ltsvcmon_txt = json["svc_ltsvcmon"]["Status"]; + var ltsvcmon_txt = json['svc_ltsvcmon']['Status'] } if ( - json["svc_ltsvcmon"]["Status"] == "Running" && - json["svc_ltsvcmon"]["Start Mode"] == "Auto" + json['svc_ltsvcmon']['Status'] == 'Running' && + json['svc_ltsvcmon']['Start Mode'] == 'Auto' ) { - var ltsvcmon_status = ""; + var ltsvcmon_status = "" } else { - var ltsvcmon_status = ""; + var ltsvcmon_status = "" } - SC.ui.addElement($("dataTable"), "tr", { id: "ltsvcmon_row" }); - SC.ui.addElement($("ltsvcmon_row"), "th", { - id: "agent_id_hdr", - innerHTML: "SVC - LTSVCMon", - }); - SC.ui.addElement($("ltsvcmon_row"), "td", { - id: "ltsvc", - innerHTML: ltsvcmon_status + " " + ltsvcmon_txt, - }); + SC.ui.addElement($('dataTable'), 'tr', { id: 'ltsvcmon_row' }) + SC.ui.addElement($('ltsvcmon_row'), 'th', { + id: 'agent_id_hdr', + innerHTML: 'SVC - LTSVCMon' + }) + SC.ui.addElement($('ltsvcmon_row'), 'td', { + id: 'ltsvc', + innerHTML: ltsvcmon_status + ' ' + ltsvcmon_txt + }) } - if ("ltposh_loaded" in json) { - SC.ui.addElement($("dataTable"), "tr", { id: "ltposh_row" }); - SC.ui.addElement($("ltposh_row"), "th", { - id: "ltposh_hdr", - innerHTML: "LTPosh Loaded", - }); - SC.ui.addElement($("ltposh_row"), "td", { - id: "ltposh", - innerHTML: json["ltposh_loaded"] + if ('ltposh_loaded' in json) { + SC.ui.addElement($('dataTable'), 'tr', { id: 'ltposh_row' }) + SC.ui.addElement($('ltposh_row'), 'th', { + id: 'ltposh_hdr', + innerHTML: 'LTPosh Loaded' + }) + SC.ui.addElement($('ltposh_row'), 'td', { + id: 'ltposh', + innerHTML: json['ltposh_loaded'] ? " PowerShell Module Loaded" : " Failed to load PowerShell Module", - colspan: 2, - }); + colspan: 2 + }) } - if ("repair" in json) { - SC.ui.addElement($("dataTable"), "tr", { id: "repair_row" }); - SC.ui.addElement($("repair_row"), "th", { - id: "repair_hdr", - innerHTML: "Recommended Repair", - }); - SC.ui.addElement($("repair_row"), "td", { - id: "repair_val", - innerHTML: json["repair"], - colspan: 2, - }); + if ('repair' in json) { + SC.ui.addElement($('dataTable'), 'tr', { id: 'repair_row' }) + SC.ui.addElement($('repair_row'), 'th', { + id: 'repair_hdr', + innerHTML: 'Recommended Repair' + }) + SC.ui.addElement($('repair_row'), 'td', { + id: 'repair_val', + innerHTML: json['repair'], + colspan: 2 + }) } - SC.ui.addElement($("repairOptions"), "DIV", { - id: "repairDiv", + //2023.01.18 -- Joe McCall | Implemented native HTML classes for formatting. + SC.ui.addElement($('repairOptions'), 'DIV', { + id: 'repairDiv', innerHTML: '

Repair Options

', - className: "Header", - }); - var repairCol1 = SC.ui.addElement($("repairOptions"), "div", { - id: "restartOption", - className: "DiagActions", - }); - var repairCol2 = SC.ui.addElement($("repairOptions"), "div", { - id: "reinstallOption", - className: "DiagActions", - }); - SC.command.queryAndAddCommandButtons(repairCol1, "RestartButton"); - SC.command.queryAndAddCommandButtons(repairCol2, "ReinstallButton"); - - SC.ui.addElement($("repairOptions"), "DIV", { - id: "lterrorsDiv", + className: 'Header' + }) + var repairCol1 = SC.ui.addElement($('repairOptions'), 'div', { + id: 'restartOption', + className: 'DiagActions' + }) + var repairCol2 = SC.ui.addElement($('repairOptions'), 'div', { + id: 'reinstallOption', + className: 'DiagActions' + }) + SC.command.queryAndAddCommandButtons(repairCol1, 'RestartButton') + SC.command.queryAndAddCommandButtons(repairCol2, 'ReinstallButton') + + SC.ui.addElement($('repairOptions'), 'DIV', { + id: 'lterrorsDiv', innerHTML: '

Agent Log

', - className: "Header", - }); - if ("lterrors" in json && json["lterrors"] != "") { - SC.ui.addElement($("lterrors"), "pre", { - id: "lterrors_file", - innerHTML: atob(json["lterrors"]), - }); + className: 'Header' + }) + if ('lterrors' in json && json['lterrors'] != '') { + SC.ui.addElement($('lterrors'), 'pre', { + id: 'lterrors_file', + innerHTML: atob(json['lterrors']) + }) } else { - SC.ui.addElement($("lterrors"), "pre", { - id: "lterrors_file", - innerHTML: "Click 'Run CWA Diagnostic' to pull in the latest log file.", - }); + SC.ui.addElement($('lterrors'), 'pre', { + id: 'lterrors_file', + innerHTML: "Click 'Run CWA Diagnostic' to pull in the latest log file." + }) } } -function isUsingInternetExplorerOrEdge() { - var ua = window.navigator.userAgent; - var msie = ua.indexOf("Trident"); +function isUsingInternetExplorerOrEdge () { + var ua = window.navigator.userAgent + var msie = ua.indexOf('Trident') - if (ua.indexOf("Trident") > 0 || ua.indexOf("Edge") > 0) return true; - else return false; + if (ua.indexOf('Trident') > 0 || ua.indexOf('Edge') > 0) return true + else return false } //ripped directly from http://stackoverflow.com/questions/6108819/javascript-timestamp-to-relative-time-eg-2-seconds-ago-one-week-ago-etc-best -function timeDifference(current, previous) { - var msPerMinute = 60 * 1000; - var msPerHour = msPerMinute * 60; - var msPerDay = msPerHour * 24; - var msPerMonth = msPerDay * 30; - var msPerYear = msPerDay * 365; +function timeDifference (current, previous) { + var msPerMinute = 60 * 1000 + var msPerHour = msPerMinute * 60 + var msPerDay = msPerHour * 24 + var msPerMonth = msPerDay * 30 + var msPerYear = msPerDay * 365 - var elapsed = current - previous; + var elapsed = current - previous if (elapsed < msPerMinute) - return Math.abs(Math.round(elapsed / 1000)) + " seconds ago"; + return Math.abs(Math.round(elapsed / 1000)) + ' seconds ago' else if (elapsed < msPerHour) - return Math.round(elapsed / msPerMinute) + " minutes ago"; + return Math.round(elapsed / msPerMinute) + ' minutes ago' else if (elapsed < msPerDay) - return Math.round(elapsed / msPerHour) + " hours ago"; + return Math.round(elapsed / msPerHour) + ' hours ago' else if (elapsed < msPerMonth) - return "approximately " + Math.round(elapsed / msPerDay) + " days ago"; + return 'approximately ' + Math.round(elapsed / msPerDay) + ' days ago' else if (elapsed < msPerYear) - return "approximately " + Math.round(elapsed / msPerMonth) + " months ago"; - else return "approximately " + Math.round(elapsed / msPerYear) + " years ago"; + return 'approximately ' + Math.round(elapsed / msPerMonth) + ' months ago' + else return 'approximately ' + Math.round(elapsed / msPerYear) + ' years ago' } -// 2023.01.16 -- Joe McCall | Expanded cases and variables to add OS check so Mac and Linux commands are distinct (sh or python) -function getAutomateCommandText(headers) { +function getAutomateCommandText (headers) { + console.log( + 'OS: ' + headers.OperatingSystem + ' | Command: ' + headers.DiagnosticType + ) + switch ( headers.Processor + - "/" + + '/' + headers.OperatingSystem + - "/" + + '/' + headers.Interface + - "/" + + '/' + headers.ContentType + - "/" + + '/' + headers.DiagnosticType ) { - case "ps/Windows/powershell/json/Automate": + case 'ps/Windows/powershell/json/Automate': return ( - "$WarningPreference='SilentlyContinue'; IF([Net.SecurityProtocolType]::Tls) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls}; IF([Net.SecurityProtocolType]::Tls11) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls11}; IF([Net.SecurityProtocolType]::Tls12) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12}; Try { (new-object Net.WebClient).DownloadString('" + + "$WarningPreference='SilentlyContinue'; [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Try { (new-object Net.WebClient).DownloadString('" + getAutomateDiagnosticsURL() + "') | iex; Start-AutomateDiagnostics -ltposh '" + getLTPoSh() + @@ -776,44 +803,84 @@ function getAutomateCommandText(headers) { getLTServer() + "' " + getVerbose() + - "} Catch { $_.Exception.Message; Write-Output '!---BEGIN JSON---!'; Write-Output '{\"version\": \"Error loading AutomateDiagnostics\"}' }" - ); - case "ps/Windows/powershell/json/RestartAutomate": + '} Catch { $_.Exception.Message; Write-Output \'!---BEGIN JSON---!\'; Write-Output \'{"version": "Error loading AutomateDiagnostics"}\' }' + ) + + case 'ps/Windows/powershell/json/RestartAutomate': return ( - "$WarningPreference='SilentlyContinue'; IF([Net.SecurityProtocolType]::Tls) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls}; IF([Net.SecurityProtocolType]::Tls11) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls11}; IF([Net.SecurityProtocolType]::Tls12) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12}; (new-object Net.WebClient).DownloadString('" + + "$WarningPreference='SilentlyContinue'; [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; (new-object Net.WebClient).DownloadString('" + getLTPoSh() + "') | iex; Restart-LTService" - ); - case "ps/Windows/powershell/json/ReinstallAutomate": - var txtlocationid = $("#locationidreinstall").value; - var txtinstallertoken = $("#installertoken").value; + ) + + case 'ps/Windows/powershell/json/ReinstallAutomate': + var txtlocationid = $('#locationidreinstall').value + var txtinstallertoken = $('#installertoken').value if (isNaN(txtlocationid)) { - txtlocationid = "1"; + txtlocationid = '1' } return ( - "$WarningPreference='SilentlyContinue'; IF([Net.SecurityProtocolType]::Tls) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls}; IF([Net.SecurityProtocolType]::Tls11) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls11}; IF([Net.SecurityProtocolType]::Tls12) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12};(new-object Net.WebClient).DownloadString('" + + "$WarningPreference='SilentlyContinue'; [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; (new-object Net.WebClient).DownloadString('" + getLTPoSh() + "') | iex; Reinstall-LTService -SkipDotNet -Server https://" + getLTServer() + - " -LocationID " + + ' -LocationID ' + txtlocationid + - " -InstallerToken " + + ' -InstallerToken ' + txtinstallertoken - ); - return; - case "sh/Linux/bash/json/Automate": + ) + + case 'sh/Linux/bash/json/Automate': return ( - "url=" + + 'url=' + getLinuxDiagnosticsURL() + - "; CURL=$(command -v curl); WGET=$(command -v wget); if [ ! -z $CURL ]; then echo $($CURL -s $url | python - -e); else echo $($WGET -q -O - --no-check-certificate $url | python - -e); fi" - ); - case "sh/Mac/bash/json/Automate": + '; CURL=$(command -v curl); WGET=$(command -v wget); if [ ! -z $CURL ]; then echo $($CURL -s $url | python3 - -e); else echo $($WGET -q -O - --no-check-certificate $url | python3 - -e); fi' + ) + + case 'sh/Mac/bash/json/Automate': return ( - "url=" + + 'url=' + getMacDiagnosticsURL() + - "; CURL=$(command -v curl); WGET=$(command -v wget); if [ ! -z $CURL ]; then echo $($CURL -s $url | sh); else echo $($WGET -q -O - --no-check-certificate $url | sh); fi" - ); + '; CURL=$(command -v curl); WGET=$(command -v wget); if [ ! -z $CURL ]; then echo $($CURL -s $url | sh); else echo $($WGET -q -O - --no-check-certificate $url | sh); fi' + ) + + case 'sh/Linux/bash/json/RestartAutomate': + return 'service ltechagent stop ; service ltechagent start' + + case 'sh/Mac/bash/json/RestartAutomate': + return 'launchctl stop com.labtechsoftware.LTSvc ; launchctl start com.labtechsoftware.LTSvc' + + case 'sh/Linux/bash/json/ReinstallAutomate': + var txtlocationid = $('#locationidreinstall').value + var txtinstallertoken = $('#installertoken').value + if (isNaN(txtlocationid)) { + txtlocationid = '1' + } + return ( + //'cd /usr/local/ltechagent; ./uninstaller.sh; cd /tmp; rm -f LTechAgent.zip; rm -r -f LTechAgent; curl -sS -o LTechAgent.zip https://' + + 'cd /tmp; rm -f LTechAgent.zip; rm -r -f LTechAgent; curl -sS -o LTechAgent.zip https://' + + getLTServer() + + '/LabTech/Deployment.aspx?InstallerToken=' + + txtinstallertoken + + '; unzip LTechAgent.zip; cd LTechAgent; chmod +x ./install.sh; sudo ./install.sh; service ltechagent status; ps -ax | grep ltech' + ) + + case 'sh/Mac/bash/json/ReinstallAutomate': + var txtlocationid = $('#locationidreinstall').value + var txtinstallertoken = $('#installertoken').value + if (isNaN(txtlocationid)) { + txtlocationid = '1' + } + return ( + //'sudo sh /usr/local/ltechagent/uninstaller.sh; mkdir /tmp/ltagent; cd /tmp/ltagent; curl -sS -O ltagent.zip https://' + + 'mkdir /tmp/ltagent; cd /tmp/ltagent; curl -sS -O ltagent.zip https://' + + getLTServer() + + '/LabTech/Deployment.aspx?InstallerToken=' + + txtinstallertoken + + '; unzip ltagent.zip; sudo installer -pkg ltsvc.mpkg -target /; rm *; cd .; rmdir /tmp/ltagent; sudo launchctl list | grep com.labtechsoftware' + ) + default: - throw "unknown os"; + throw 'unknown os' } -} +} \ No newline at end of file diff --git a/Manifest.xml b/Manifest.xml index ebb6c6d..5bfff4c 100644 --- a/Manifest.xml +++ b/Manifest.xml @@ -1,6 +1,6 @@ - 1.0.7.3 + 1.0.7.2 Automate Diagnostics John Duprey - Complete Network Diagnoses Automate Agents from CW Control. Visit https://github.com/johnduprey/CWCAutomateDiagnostics for the Readme. @@ -35,6 +35,18 @@ Generate a long lived InstallerToken - https://www.mspgeek.com/files/file/50-generate-agent-installertoken/ + + Generate a long lived InstallerToken - https://www.mspgeek.com/files/file/50-generate-agent-installertoken/ + + + + Generate a long lived InstallerToken - https://www.mspgeek.com/files/file/50-generate-agent-installertoken/ + + + + Default LocationID to use for Reinstall if no LocationID is detected. + 1 + 1 = Enable, 0 = Disable 0 diff --git a/README.md b/README.md index 1970e68..e7c7869 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Run ConnectWise Automate agent diagnostics from ConnectWise Control. This extens | Diagnostic Details | Agent Logs | | ------------- | ------------- | -| ![Details](https://user-images.githubusercontent.com/41485711/212959588-a29b5173-bf9f-427d-9ae8-47a5c2593143.png) | ![LTRrrors](https://user-images.githubusercontent.com/41485711/212806625-1f95e9a1-3c16-489b-9219-5a90a36a4f3f.png) | +| ![Details](https://user-images.githubusercontent.com/41485711/213599129-03b69221-32e0-4bb8-9088-fc6f84ba4f90.png) | ![LTRrrors](https://user-images.githubusercontent.com/41485711/213599159-18b5419a-3f7a-4ce7-b18e-18e4c288b921.png) | ## Troubleshooting diff --git a/SessionEventTrigger.cs b/SessionEventTrigger.cs index e7b2637..db960e2 100644 --- a/SessionEventTrigger.cs +++ b/SessionEventTrigger.cs @@ -1,328 +1,223 @@ using System; -using System.Collections; -using System.Collections.Generic; using System.IO; +using System.Collections.Generic; +using System.Text; +using ScreenConnect; using System.Linq; -using System.Reflection; +using System.Collections; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; -using System.Text; using System.Text.RegularExpressions; +using System.Reflection; using System.Threading.Tasks; -using ScreenConnect; -public class -SessionEventTriggerAccessor -: IAsyncDynamicEventTrigger +public class SessionEventTriggerAccessor : IAsyncDynamicEventTrigger { - public async Task - ProcessEventAsync(SessionEventTriggerEvent sessionEventTriggerEvent) - { - if ( - sessionEventTriggerEvent.SessionEvent.EventType == - SessionEventType.Connected && - sessionEventTriggerEvent.SessionConnection.ProcessType == - ProcessType.Guest && - ExtensionContext.Current.GetSettingValue("MaintenanceMode") == - "0" && - sessionEventTriggerEvent - .Session - .ActiveConnections - .Where(_ => _.ProcessType == ProcessType.Host) - .Count() == - 0 - ) - await RunDiagnostics(sessionEventTriggerEvent, - ExtensionContext.Current); - else if ( - sessionEventTriggerEvent.SessionEvent.EventType == - SessionEventType.RanCommand && - IsDiagnosticContent(sessionEventTriggerEvent.SessionEvent.Data) - ) - { - try - { - var sessionDetails = - await SessionManagerPool - .Demux - .GetSessionDetailsAsync(sessionEventTriggerEvent - .Session - .SessionID); - string output = sessionEventTriggerEvent.SessionEvent.Data; - - if (IsDiagResult(output)) - { - var data = - output - .Split(new string[] { "!---BEGIN JSON---!" }, - StringSplitOptions.None); - if (data[1] != "") - { - DiagOutput diag = Deserialize(data[1]); - var session = sessionEventTriggerEvent.Session; - if (diag.version != null) - session - .CustomPropertyValues[Int32 - .Parse(ExtensionContext - .Current - .GetSettingValue("AgentVersionCustomProperty")) - - 1] = diag.version; - - if (diag.id != null) - session - .CustomPropertyValues[Int32 - .Parse(ExtensionContext - .Current - .GetSettingValue("AgentIDCustomProperty")) - - 1] = diag.id; - - await SessionManagerPool - .Demux - .UpdateSessionAsync("AutomateDiagnostics", - session.SessionID, - ExtensionContext - .Current - .GetSettingValue("SetUseMachineName") == - "1" - ? "" - : session.Name, - session.IsPublic, - session.Code, - session.CustomPropertyValues); - } - } - else if (IsRepairResult(output)) - { - await RunDiagnostics(sessionEventTriggerEvent, - ExtensionContext.Current); - } - } - catch (Exception e) - { - WriteLog(e.Message); - } - } - } - - public DiagOutput Deserialize(string json) - { - DataContractJsonSerializer ser = - new DataContractJsonSerializer(typeof (DiagOutput)); - using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json))) - { - return ser.ReadObject(ms) as DiagOutput; - } - } - - // 2023.01.16 -- Joe McCall | Imported the newer ASync method from DEV branch - // 2023.01.19 -- swlinak | changed method prototype to return Task, can cause compiler issues if attempting to return void - private async Task - RunDiagnostics( - SessionEventTriggerEvent sessionEventTriggerEvent, - ExtensionContext extensionContext - ) - { - var sessionDetails = - await SessionManagerPool - .Demux - .GetSessionDetailsAsync(sessionEventTriggerEvent - .Session - .SessionID); - if (sessionDetails.Session.SessionType == SessionType.Access) - { - var ltposh = extensionContext.GetSettingValue("PathToLTPoSh"); - var diag = extensionContext.GetSettingValue("PathToDiag"); - var linuxdiag = extensionContext.GetSettingValue("PathToLinuxDiag"); - var macdiag = extensionContext.GetSettingValue("PathToMacDiag"); - var server = extensionContext.GetSettingValue("AutomateHostname"); - var os = sessionDetails.Session.GuestInfo.OperatingSystemName; - var timeout = extensionContext.GetSettingValue("Timeout"); - var command = ""; - - // 2023.01.16 -- Joe McCall | Expanded with distinct option for MacOSX and Linux - if (os.Contains("Windows")) - { - command = - "#!ps\n#maxlength=100000\n#timeout=" + - timeout + - "\necho 'DIAGNOSTIC-RESPONSE/1'\necho 'DiagnosticType: Automate'\necho 'ContentType: json'\necho ''\n$WarningPreference='SilentlyContinue'; IF([Net.SecurityProtocolType]::Tls) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls}; IF([Net.SecurityProtocolType]::Tls11) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls11}; IF([Net.SecurityProtocolType]::Tls12) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12}; Try {(new-object Net.WebClient).DownloadString('" + - diag + - "') | iex; Start-AutomateDiagnostics -ltposh '" + - ltposh + - "' -automate_server '" + - server + - "'} Catch { $_.Exception.Message; Write-Output '!---BEGIN JSON---!'; Write-Output '{\"version\": \"Error loading AutomateDiagnostics\"}' }"; - } - else if (os.Contains("Mac")) - { - // 2023.01.16 -- Joe McCall | Calling the AutomateDiagnostics.sh sourced from here: https://github.com/noaht8um/CWCAutomateDiagnostics/ - command = - "#!sh\n#maxlength=100000\n#timeout=" + - timeout + - "\necho 'DIAGNOSTIC-RESPONSE/1'\necho 'DiagnosticType: Automate'\necho 'ContentType: json'\nurl=" + - macdiag + - "; CURL=$(command -v curl); WGET=$(command -v wget); if [ ! -z $CURL ]; then echo $($CURL -s $url | sh); else echo $($WGET -q -O - --no-check-certificate $url | sh); fi"; - } - else if (os.Contains("Linux")) - { - command = - "#!sh\n#maxlength=100000\n#timeout=" + - timeout + - "\necho 'DIAGNOSTIC-RESPONSE/1'\necho 'DiagnosticType: Automate'\necho 'ContentType: json'\nurl=" + - linuxdiag + - "; CURL=$(command -v curl); WGET=$(command -v wget); if [ ! -z $CURL ]; then echo $($CURL -s $url | python); else echo $($WGET -q -O - --no-check-certificate $url | python); fi"; - } - else - { - command = - "@echo off\necho No OS Detected, try running the diagnostic again"; - } - - await SessionManagerPool - .Demux - .AddSessionEventAsync(sessionEventTriggerEvent - .Session - .SessionID, - SessionEventType.QueuedCommand, - SessionEventAttributes.NeedsProcessing, - "AutomateDiagnostics", - command); - } - } - - private bool IsDiagnosticContent(string eventData) - { - if ( - eventData.StartsWith("DIAGNOSTIC-RESPONSE/1") || - eventData.StartsWith("\ufeffDIAGNOSTIC-RESPONSE/1") - ) - { - return true; - } - else - { - return false; - } - } - - private bool IsRepairResult(string eventData) - { - if ( - eventData.Contains("DiagnosticType: ReinstallAutomate") || - eventData.Contains("DiagnosticType: RestartAutomate") - ) - { - return true; - } - else - { - return false; - } - } - - private bool IsDiagResult(string eventData) - { - var data = - eventData - .Split(new string[] { "!---BEGIN JSON---!" }, - StringSplitOptions.None); - if (data[1] != "") - { - if (data[0].Contains("DiagnosticType: Automate")) - { - return true; - } - else - { - return false; - } - } - else - { - return false; - } - } - - private static string FormatMessage(string message) - { - DateTime now = DateTime.Now; - return string.Format("{0}: {1}", now.ToString(), message); - } - - public static void WriteLog(string message) - { - try - { - using ( - StreamWriter streamWriter = - new StreamWriter(string - .Concat(Environment - .ExpandEnvironmentVariables("%windir%"), - "\\temp\\AutomateDiagnostics.log"), - true) - ) - { - streamWriter.WriteLine(FormatMessage(message)); - } - } - catch - { - } - } - - public static void var_dump(object obj) - { - WriteLog(String.Format("{0,-18} {1}", "Name", "Value")); - string ln = - @"-----------------------------------------------------------------"; - WriteLog (ln); - - Type t = obj.GetType(); - PropertyInfo[] props = t.GetProperties(); - - for (int i = 0; i < props.Length; i++) - { - try - { - WriteLog(String - .Format("{0,-18} {1}", - props[i].Name, - props[i].GetValue(obj, null))); - } - catch (Exception e) - { - //Console.WriteLine(e); - } - } - } + public async Task ProcessEventAsync(SessionEventTriggerEvent sessionEventTriggerEvent) + { + if (sessionEventTriggerEvent.SessionEvent.EventType == SessionEventType.Connected + && sessionEventTriggerEvent.SessionConnection.ProcessType == ProcessType.Guest + && ExtensionContext.Current.GetSettingValue("MaintenanceMode") == "0" + && sessionEventTriggerEvent.Session.ActiveConnections.Where(_ => _.ProcessType == ProcessType.Host).Count() == 0) + await RunDiagnostics(sessionEventTriggerEvent, ExtensionContext.Current); + else if (sessionEventTriggerEvent.SessionEvent.EventType == SessionEventType.RanCommand && IsDiagnosticContent(sessionEventTriggerEvent.SessionEvent.Data)) + { + try + { + var sessionDetails = await SessionManagerPool.Demux.GetSessionDetailsAsync(sessionEventTriggerEvent.Session.SessionID); + string output = sessionEventTriggerEvent.SessionEvent.Data; + + if (IsDiagResult(output)) + { + var data = output.Split(new string[] { "!---BEGIN JSON---!" }, StringSplitOptions.None); + if (data[1] != "") + { + DiagOutput diag = Deserialize(data[1]); + var session = sessionEventTriggerEvent.Session; + if (diag.version != null) + session.CustomPropertyValues[Int32.Parse(ExtensionContext.Current.GetSettingValue("AgentVersionCustomProperty")) - 1] = diag.version; + + if (diag.id != null) + session.CustomPropertyValues[Int32.Parse(ExtensionContext.Current.GetSettingValue("AgentIDCustomProperty")) - 1] = diag.id; + + await SessionManagerPool.Demux.UpdateSessionAsync( + "AutomateDiagnostics", + session.SessionID, + ExtensionContext.Current.GetSettingValue("SetUseMachineName") == "1" ? "" : sessionEventTriggerEvent.Session.Name, + session.IsPublic, + session.Code, + session.CustomPropertyValues + ); + } + } + else if (IsRepairResult(output)) + { + await RunDiagnostics(sessionEventTriggerEvent, ExtensionContext.Current); + } + } + catch (Exception e) + { + WriteLog(e.Message); + } + + } + } + + public DiagOutput Deserialize(string json) + { + DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(DiagOutput)); + using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + return ser.ReadObject(ms) as DiagOutput; + } + } + + // 2023.01.16 -- Joe McCall | Imported the newer ASync method from DEV branch + // 2023.01.19 -- swlinak | changed method prototype to return Task, can cause compiler issues if attempting to return void + private async Task RunDiagnostics(SessionEventTriggerEvent sessionEventTriggerEvent, ExtensionContext extensionContext) + { + var sessionDetails = await SessionManagerPool.Demux.GetSessionDetailsAsync(sessionEventTriggerEvent.Session.SessionID); + if (sessionDetails.Session.SessionType == SessionType.Access) + { + var ltposh = extensionContext.GetSettingValue("PathToLTPoSh"); + var diag = extensionContext.GetSettingValue("PathToDiag"); + var linuxdiag = extensionContext.GetSettingValue("PathToLinuxDiag"); + var macdiag = extensionContext.GetSettingValue("PathToMacDiag"); + var server = extensionContext.GetSettingValue("AutomateHostname"); + var os = sessionDetails.Session.GuestInfo.OperatingSystemName; + var timeout = extensionContext.GetSettingValue("Timeout"); + var command = ""; + + // 2023.01.16 -- Joe McCall | Expanded with distinct option for MacOSX and Linux + if (os.Contains("Windows")) + { + command = "#!ps\n#maxlength=100000\n#timeout=" + timeout + "\necho 'DIAGNOSTIC-RESPONSE/1'\necho 'DiagnosticType: Automate'\necho 'ContentType: json'\necho ''\n$WarningPreference='SilentlyContinue'; IF([Net.SecurityProtocolType]::Tls) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls}; IF([Net.SecurityProtocolType]::Tls11) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls11}; IF([Net.SecurityProtocolType]::Tls12) {[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12}; Try {(new-object Net.WebClient).DownloadString('" + diag + "') | iex; Start-AutomateDiagnostics -ltposh '" + ltposh + "' -automate_server '" + server + "'} Catch { $_.Exception.Message; Write-Output '!---BEGIN JSON---!'; Write-Output '{\"version\": \"Error loading AutomateDiagnostics\"}' }"; + } + else if (os.Contains("Mac")) + { + // 2023.01.16 -- Joe McCall | Calling the AutomateDiagnostics.sh sourced from here: https://github.com/noaht8um/CWCAutomateDiagnostics/ + command = "#!sh\n#maxlength=100000\n#timeout=" + timeout + "\necho 'DIAGNOSTIC-RESPONSE/1'\necho 'DiagnosticType: Automate'\necho 'ContentType: json'\nurl=" + macdiag + "; CURL=$(command -v curl); WGET=$(command -v wget); if [ ! -z $CURL ]; then echo $($CURL -s $url | sh); else echo $($WGET -q -O - --no-check-certificate $url | sh); fi"; + } + else if (os.Contains("Linux")) + { + command = "#!sh\n#maxlength=100000\n#timeout=" + timeout + "\necho 'DIAGNOSTIC-RESPONSE/1'\necho 'DiagnosticType: Automate'\necho 'ContentType: json'\nurl=" + linuxdiag + "; CURL=$(command -v curl); WGET=$(command -v wget); if [ ! -z $CURL ]; then echo $($CURL -s $url | python); else echo $($WGET -q -O - --no-check-certificate $url | python); fi"; + } + else { command = "@echo off\necho No OS Detected, try running the diagnostic again"; } + + await SessionManagerPool.Demux.AddSessionEventAsync( + sessionEventTriggerEvent.Session.SessionID, + SessionEventType.QueuedCommand, + SessionEventAttributes.NeedsProcessing, + "AutomateDiagnostics", + command + ); + } + } + + private bool IsDiagnosticContent(string eventData) + { + if (eventData.StartsWith("DIAGNOSTIC-RESPONSE/1") || eventData.StartsWith("\ufeffDIAGNOSTIC-RESPONSE/1")) + { + return true; + } + else + { + return false; + } + } + + private bool IsRepairResult(string eventData) + { + if (eventData.Contains("DiagnosticType: ReinstallAutomate") || eventData.Contains("DiagnosticType: RestartAutomate")) + { + return true; + } + else + { + return false; + } + } + + private bool IsDiagResult(string eventData) + { + if (eventData.Contains("DiagnosticType: Automate")) + { + return true; + } + else + { + return false; + } + } + + private static string FormatMessage(string message) + { + DateTime now = DateTime.Now; + return string.Format("{0}: {1}", now.ToString(), message); + } + + public static void WriteLog(string message) + { + try + { + using (StreamWriter streamWriter = new StreamWriter(string.Concat(Environment.ExpandEnvironmentVariables("%windir%"), "\\temp\\AutomateDiagnostics.log"), true)) + { + streamWriter.WriteLine(FormatMessage(message)); + } + } + catch { } + } + + public static void var_dump(object obj) + { + WriteLog(String.Format("{0,-18} {1}", "Name", "Value")); + string ln = @"-----------------------------------------------------------------"; + WriteLog(ln); + + Type t = obj.GetType(); + PropertyInfo[] props = t.GetProperties(); + + for (int i = 0; i < props.Length; i++) + { + try + { + WriteLog(String.Format("{0,-18} {1}", + props[i].Name, props[i].GetValue(obj, null))); + } + catch (Exception e) + { + //Console.WriteLine(e); + } + } + } } public class DiagOutput { - [DataMember(Name = "id", IsRequired = false)] - public String id; + [DataMember(Name = "id", IsRequired = false)] + public String id; - [DataMember(Name = "version", IsRequired = false)] - public String version; + [DataMember(Name = "version", IsRequired = false)] + public String version; - [DataMember(Name = "server_addr", IsRequired = false)] - public String server_addr; + [DataMember(Name = "server_addr", IsRequired = false)] + public String server_addr; - [DataMember(Name = "online", IsRequired = false)] - public Boolean online; + [DataMember(Name = "online", IsRequired = false)] + public Boolean online; - [DataMember(Name = "heartbeat", IsRequired = false)] - public Boolean heartbeat; + [DataMember(Name = "heartbeat", IsRequired = false)] + public Boolean heartbeat; - [DataMember(Name = "lastcontact", IsRequired = false)] - public String lastcontact; + [DataMember(Name = "lastcontact", IsRequired = false)] + public String lastcontact; - [DataMember(Name = "heartbeat_sent", IsRequired = false)] - public String heartbeat_sent; + [DataMember(Name = "heartbeat_sent", IsRequired = false)] + public String heartbeat_sent; - [DataMember(Name = "heartbeat_rcv", IsRequired = false)] - public String heartbeat_rcv; + [DataMember(Name = "heartbeat_rcv", IsRequired = false)] + public String heartbeat_rcv; - [DataMember(Name = "ltposh_loaded", IsRequired = true)] - public Boolean ltposh_loaded; -} + [DataMember(Name = "ltposh_loaded", IsRequired = true)] + public Boolean ltposh_loaded; +} \ No newline at end of file