Update module/Samy.Http.ps1
This commit is contained in:
@@ -1,220 +0,0 @@
|
||||
# Samy.Http.ps1
|
||||
# HTTP helpers, listener, and dispatcher
|
||||
|
||||
function Send-Text {
|
||||
param($Context, $Text)
|
||||
if (-not $Context -or -not $Context.Response) {
|
||||
return
|
||||
}
|
||||
$bytes = [Text.Encoding]::UTF8.GetBytes([string]$Text)
|
||||
$Context.Response.ContentType = 'text/plain'
|
||||
$Context.Response.ContentLength64 = $bytes.Length
|
||||
$Context.Response.OutputStream.Write($bytes,0,$bytes.Length)
|
||||
$Context.Response.OutputStream.Close()
|
||||
}
|
||||
|
||||
function Send-HTML {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][object] $Context,
|
||||
[Parameter(Mandatory = $true)][string] $Html
|
||||
)
|
||||
if (-not $Context -or -not $Context.Response) {
|
||||
return
|
||||
}
|
||||
$bytes = [Text.Encoding]::UTF8.GetBytes($Html)
|
||||
$Context.Response.ContentType = 'text/html'
|
||||
$Context.Response.ContentLength64 = $bytes.Length
|
||||
$Context.Response.OutputStream.Write($bytes, 0, $bytes.Length)
|
||||
$Context.Response.OutputStream.Close()
|
||||
}
|
||||
|
||||
function Send-JSON {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
$Context,
|
||||
$Object
|
||||
)
|
||||
|
||||
if (-not $Context -or -not $Context.Response) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if ($null -eq $Object) {
|
||||
Write-LogHybrid "Send-JSON called with `$null object; returning empty JSON array." Warning Printers -LogToEvent
|
||||
$json = '[]'
|
||||
}
|
||||
else {
|
||||
try {
|
||||
$json = $Object | ConvertTo-Json -Depth 5 -ErrorAction Stop
|
||||
}
|
||||
catch {
|
||||
Write-LogHybrid "Send-JSON serialization failed: $($_.Exception.Message); returning empty JSON array." Error Printers -LogToEvent
|
||||
$json = '[]'
|
||||
}
|
||||
}
|
||||
|
||||
$json = [string]$json
|
||||
$bytes = [Text.Encoding]::UTF8.GetBytes($json)
|
||||
$Context.Response.ContentType = 'application/json'
|
||||
$Context.Response.ContentLength64 = $bytes.Length
|
||||
$Context.Response.OutputStream.Write($bytes, 0, $bytes.Length)
|
||||
$Context.Response.OutputStream.Close()
|
||||
}
|
||||
catch {
|
||||
Write-LogHybrid "Send-JSON fatal error: $($_.Exception.Message)" Error Printers -LogToEvent
|
||||
try {
|
||||
$fallback = '[]'
|
||||
$bytes = [Text.Encoding]::UTF8.GetBytes($fallback)
|
||||
$Context.Response.ContentType = 'application/json'
|
||||
$Context.Response.ContentLength64 = $bytes.Length
|
||||
$Context.Response.OutputStream.Write($bytes, 0, $bytes.Length)
|
||||
$Context.Response.OutputStream.Close()
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-TasksCompleted {
|
||||
param($Context)
|
||||
|
||||
Write-LogHybrid "All UI-selected tasks processed" Info UI -LogToEvent
|
||||
Send-Text $Context "Tasks completion acknowledged."
|
||||
}
|
||||
|
||||
function Get-NextFreePort {
|
||||
param([int]$Start)
|
||||
|
||||
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."
|
||||
}
|
||||
|
||||
function Dispatch-Request {
|
||||
param($Context)
|
||||
|
||||
$path = $Context.Request.Url.AbsolutePath.TrimStart('/')
|
||||
|
||||
if ($path -eq 'quit') {
|
||||
Write-LogHybrid "Shutdown requested" Info Server -LogToEvent
|
||||
Send-Text $Context "Server shutting down."
|
||||
$Global:Listener.Stop()
|
||||
return
|
||||
}
|
||||
|
||||
if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'tasksCompleted') {
|
||||
Invoke-TasksCompleted $Context
|
||||
return
|
||||
}
|
||||
|
||||
if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'getpw') {
|
||||
Invoke-FetchSites $Context
|
||||
return
|
||||
}
|
||||
|
||||
if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'renameComputer') {
|
||||
Invoke-RenameComputer $Context
|
||||
return
|
||||
}
|
||||
|
||||
if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'getprinters') {
|
||||
Invoke-GetPrinters $Context
|
||||
return
|
||||
}
|
||||
|
||||
if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'installprinters') {
|
||||
Invoke-InstallPrinters $Context
|
||||
return
|
||||
}
|
||||
|
||||
if ($path -in @('', 'onboard', 'offboard', 'tweaks', 'SVSApps')) {
|
||||
$page = if ($path -eq '') { 'onboard' } else { $path }
|
||||
$html = Get-UIHtml -Page $page
|
||||
Send-HTML $Context $html
|
||||
return
|
||||
}
|
||||
|
||||
$task = $Global:SamyTasks | Where-Object Name -EQ $path
|
||||
if ($task) {
|
||||
& $task.HandlerFn $Context
|
||||
return
|
||||
}
|
||||
|
||||
$Context.Response.StatusCode = 404
|
||||
Send-Text $Context '404 - Not Found'
|
||||
}
|
||||
|
||||
function Start-SamyHttpServer {
|
||||
param(
|
||||
[Parameter(Mandatory)][int]$Port
|
||||
)
|
||||
|
||||
$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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user