diff --git a/StackMonkey.ps1 b/StackMonkey.ps1 index bef2a8e..824def9 100644 --- a/StackMonkey.ps1 +++ b/StackMonkey.ps1 @@ -308,7 +308,7 @@ $ConfirmPreference = 'None' #region Write-Log # This function is used as a fallback if the SVSMSP module is not installed - # This function is used as a fallback if the SVSMSP module is not installed + # Should change this "[string]$EventLog = "Application", => [string]$EventLog = "SVS Scripting", " function Write-LogHelper { [CmdletBinding()] param( @@ -393,6 +393,7 @@ $ConfirmPreference = 'None' # ───────────────────────────────────────────────────────────────────────── # WRITE-LOG HYBRID (single definition, chooses at runtime if we use the # Write-Log from the module or the built-in Write-LogHelper funtions ) + # Should chanfge this "[string]$EventLog = "Application"," => "[string]$EventLog = "SVS Scripting"," # ───────────────────────────────────────────────────────────────────────── function Write-LogHybrid { @@ -497,6 +498,9 @@ $ConfirmPreference = 'None' # SVS Apps @{ Id='wingetLastpass'; Name='wingetLastpass'; Label='LastPass Desktop App'; HandlerFn='Install-WingetLastPass'; Page='SVSApps' } + @{ Id='wingetChrome'; Name='wingetChrome'; Label='Google Chrome'; HandlerFn='Handle-InstallChrome'; Page='SVSApps' } + @{ Id='wingetAcrobat'; Name='wingetAcrobat'; Label='Adobe Acrobat Reader (64-bit)'; HandlerFn='Handle-InstallAcrobat'; Page='SVSApps' } + ) #endregion building the Menus @@ -556,31 +560,74 @@ $subHtml #endregion Get-ModuleVersionHtml #region Strat-Server - # Starts the HTTP listener loop - function Start-Server { - # make it accessible to Dispatch-Request - $Global:Listener = [System.Net.HttpListener]::new() - $Global:Listener.Prefixes.Add("http://localhost:$Port/") - $Global:Listener.Start() - Write-LogHybrid "Listening on http://localhost:$Port/" Info Server - - - try { - while ($Global:Listener.IsListening) { - $ctx = $Global:Listener.GetContext() - try { - Dispatch-Request $ctx - } catch { - Write-LogHybrid "Dispatch error: $_" "Error" "Server" -LogToEvent - } - } - } finally { - # once the loop exits, clean up - $Global:Listener.Close() - Write-LogHybrid "Listener closed." "Info" "Server" -LogToEvent - } + function Get-NextFreePort { + param([int]$Start = $Port) + for ($p = [Math]::Max(1024,$Start); $p -lt 65535; $p++) { + $l = [System.Net.Sockets.TcpListener]::new([Net.IPAddress]::Loopback, $p) + try { $l.Start(); $l.Stop(); return $p } catch {} + } + throw "No free TCP port available." } - #endregion Strat-Server + + # Starts the HTTP listener loop + function Start-Server { + $Global:Listener = [System.Net.HttpListener]::new() + $primaryPrefix = "http://localhost:$Port/" + $wildcardPrefix = "http://+:$Port/" + + try { + $Global:Listener.Prefixes.Add($primaryPrefix) + $Global:Listener.Start() + Write-LogHybrid "Listening on $primaryPrefix" Info Server -LogToEvent + } + catch [System.Net.HttpListenerException] { + if ($_.Exception.ErrorCode -eq 5) { + Write-LogHybrid "Access denied on $primaryPrefix. Attempting URL ACL…" Warning Server -LogToEvent + try { + $user = "$env:USERDOMAIN\$env:USERNAME" + if (-not $user.Trim()) { $user = $env:USERNAME } + Start-Process -FilePath "netsh" -ArgumentList "http add urlacl url=$wildcardPrefix user=`"$user`" listen=yes" -Verb RunAs -WindowStyle Hidden -Wait + $Global:Listener = [System.Net.HttpListener]::new() + $Global:Listener.Prefixes.Add($wildcardPrefix) + $Global:Listener.Start() + Write-LogHybrid "Listening on $wildcardPrefix (URL ACL added for $user)" Success Server -LogToEvent + } catch { + Write-LogHybrid "URL ACL registration failed: $($_.Exception.Message)" Error Server -LogToEvent + return + } + } + elseif ($_.Exception.NativeErrorCode -in 32,183) { + $old = $Port + $Port = Get-NextFreePort -Start ($Port + 1) + $Global:Listener = [System.Net.HttpListener]::new() + $primaryPrefix = "http://localhost:$Port/" + $Global:Listener.Prefixes.Add($primaryPrefix) + $Global:Listener.Start() + Write-LogHybrid "Port $old busy. Listening on $primaryPrefix" Warning Server -LogToEvent + } + else { + Write-LogHybrid "HttpListener start failed: $($_.Exception.Message)" Error Server -LogToEvent + return + } + } + + try { + while ($Global:Listener.IsListening) { + $ctx = $Global:Listener.GetContext() + try { + Dispatch-Request $ctx + } catch { + Write-LogHybrid "Dispatch error: $($_.Exception.Message)" Error Server -LogToEvent + } + } + } + finally { + $Global:Listener.Close() + Write-LogHybrid "Listener closed." Info Server -LogToEvent + } + } +#endregion Strat-Server + #region UIHtml function Get-UIHtml { @@ -1425,6 +1472,29 @@ function Handle-InstallDattoRMM { Respond-Text $Context "Internal server error during DattoRMM install." } } + +function Handle-InstallChrome { param($Context) + try { + winget install --id=Google.Chrome --silent --accept-package-agreements --accept-source-agreements + Write-LogHybrid "Installed Google Chrome via winget" Success SVSApps -LogToEvent + Respond-Text $Context "Chrome installed" + } catch { + Write-LogHybrid "Chrome install failed: $($_.Exception.Message)" Error SVSApps -LogToEvent + Respond-Text $Context "ERROR: $($_.Exception.Message)" + } +} + +function Handle-InstallAcrobat { param($Context) + try { + winget install --id=Adobe.Acrobat.Reader.64-bit --silent --accept-package-agreements --accept-source-agreements + Write-LogHybrid "Installed Adobe Acrobat Reader (64-bit) via winget" Success SVSApps -LogToEvent + Respond-Text $Context "Acrobat Reader installed" + } catch { + Write-LogHybrid "Acrobat install failed: $($_.Exception.Message)" Error SVSApps -LogToEvent + Respond-Text $Context "ERROR: $($_.Exception.Message)" + } +} + #endregion Handler Stubs #region Install-DattoRMM @@ -1560,7 +1630,7 @@ function Install-DattoRMM { $token = $tokenResp.access_token Write-LogHybrid "OAuth token acquired." Success DattoRMM -LogToEvent } catch { - Write-LogHybrid "OAuth token fetch failed: $($_.Exception.Message)" Error DattoRMM-LogToEvent; return + Write-LogHybrid "OAuth token fetch failed: $($_.Exception.Message)" Error DattoRMM -LogToEvent; return } $headers = @{ Authorization = "Bearer $token" } @@ -1754,11 +1824,20 @@ function Install-DattoRMM { } 'UI' { - Write-LogHybrid "Starting ScriptMonkey UI on http://localhost:$Port/" Info Startup - Start-Process "msedge.exe" -ArgumentList "--app=http://localhost:$Port" - Start-Server # blocks until you click Exit - return - } + Write-LogHybrid "Starting ScriptMonkey UI on http://localhost:$Port/" Info Startup + try { + Start-Server + try { + Start-Process "msedge.exe" -ArgumentList "--app=http://localhost:$Port" -ErrorAction Stop + } catch { + Start-Process "http://localhost:$Port" + } + } catch { + Write-LogHybrid "Failed to start server: $($_.Exception.Message)" Error Startup -LogToEvent + } + return + } + } #endregion EntryPoint: Define Invoke-ScriptMonkey