· 6 years ago · Jul 05, 2019, 02:48 AM
1########################Add Types###################################################
2Add-Type -AssemblyName PresentationFramework
3Add-Type -AssemblyName System.Windows.Forms
4Add-Type -Name Window -Namespace Console -MemberDefinition '
5[DllImport("Kernel32.dll")]
6public static extern IntPtr GetConsoleWindow();
7
8[DllImport("user32.dll")]
9public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
10'
11$ErrorActionPreference = "SilentlyContinue"
12
13
14If (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {[System.Windows.Forms.MessageBox]::Show("This application needs to be ran as an administrator.", "Elevated Privilages Required."); Return; Exit 5}
15
16$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
17#Write-Host "Script Path: $ScriptPath"
18
19
20
21#Quick check to make sure the machine even has a network location. Commented out to prevent other testers from getting stalled.
22If(!(Test-Connection -Cn admin.com -BufferSize 16 -Count 1 -ea 0 -quiet)) {[System.Windows.Forms.MessageBox]::Show("The Primary server is unreachable. Make sure Ethernet is connected.", "No Way to Phone Home."); Return;}
23
24If (!(Test-Path "C:\Production Base Apps")) {mkdir "C:\Production Base Apps"}
25If (!(Test-Path "C:\Wim")) {mkdir "C:\Wim"}
26
27
28If (!(Test-Path "\\$Env:Computername\Production Base Apps")) {New-SmbShare -Temporary -Name "Production Base Apps" -ReadAccess "admin\u46 imaging rights" -Path "C:\Production Base Apps"}
29If (!(Test-Path "\\$Env:Computername\Wim")) {New-SmbShare -Temporary -Name "Wim" -ReadAccess "admin\u46 imaging rights" -Path "C:\Wim"}
30Function CountConnections {
31 $smb = Get-SMBSession | Where-Object {$_.ClientComputerName -Like "10.*"}
32 $Global:SessionCount = ($smb.clientcomputername).count
33 #Check if the variable is NULL.
34 IF (!($Global:SessionCount)) {$Global:SessionCount = "0"}
35 #Change content of TextBlock to the active number of connections
36 $syncHash.TextBlock_ActiveConnections.Text = "Number of clients connected:
37 $Global:SessionCount (Maximum of 20)"
38 }
39
40Function WriteDate {
41 $SyncTime = Get-Date
42 # Write-Output $SyncTime | Out-File "C:\Program Files\CacheDevice\LastSync.Date"
43}
44#$CountConnectionsFunction = Get-Content Function:\CountConnections -ErrorAction Stop
45#$CountConnectionsObject = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList 'CountConnections', $CountConnectionsFunction
46
47$WriteDateFunction = Get-Content Function:\WriteDate -ErrorAction Stop
48$WriteDateFunctionObject = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList 'WriteDateFunction', $WriteDateFunction
49
50#Add Function to session state
51$InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
52#$InitialSessionState.Commands.Add($CountConnectionsObject)
53$InitialSessionState.Commands.Add($WriteDateFunctionObject)
54$InitialSessionState.Commands.Add($scriptPath)
55
56
57#Start runspace for the WPF GUI.
58$Runspace = [runspacefactory]::CreateRunspace($InitialSessionState)
59$Runspace.ApartmentState = "STA"
60$Runspace.ThreadOptions = "ReuseThread"
61$Runspace.Open()
62
63$code = {
64
65#Write-Host "Script Path: $ScriptPath"
66#############################XAML Code stolen from Visual Studio#############################################
67[xml]$xaml = @"
68<Window
69xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
70xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="Window_Main"
71
72Title="Image Cache Software" Height="256" Width="380" ResizeMode="NoResize">
73<Grid Name="MainMenu">
74<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Your computer must syncronize with the primary Zen server before continuing." VerticalAlignment="Top" Margin="10,10,0,0" Height="64" Width="120"/>
75<Button x:Name="Button_ShutDown" Content="Shut Down" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Margin="10,104,0,0"/>
76<Button x:Name="Button_StartCachingServices" Content="Start Caching Services" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Margin="10,79,0,0"/>
77<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Margin="140,137,0,0" Width="100" Height="25" TextAlignment="Center"><Run Text="Sync Status"/><LineBreak/><Run/></TextBlock>
78<Rectangle x:Name="Rectangle_Status" Fill="Red" HorizontalAlignment="Right" Height="25" Margin="0,137,339,0" Stroke="Black" VerticalAlignment="Top" Width="25"/>
79<Border BorderBrush="Black" BorderThickness="1" Margin="10,167,10,10">
80 <TextBlock x:Name="TextBlock_SyncStatus" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Margin="-1" Width="354" Height="69" TextAlignment="Center"><Run/><LineBreak/><Run Text="Waiting for Synchronization"/></TextBlock>
81</Border>
82<TextBlock x:Name="TextBlock_ActiveConnections" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Margin="172,10,0,0" Width="192"><Run Text="Number of clients connected:"/><LineBreak/><Run Text="(Maximum of 20)"/><LineBreak/><Run/></TextBlock>
83<Rectangle HorizontalAlignment="Left" Height="1" Margin="0,131,0,0" Stroke="Black" VerticalAlignment="Top" Width="374"/>
84<Rectangle HorizontalAlignment="Left" Height="132" Margin="139,0,0,0" Stroke="Black" VerticalAlignment="Top" Width="1" RenderTransformOrigin="0.5,0.5">
85 <Rectangle.RenderTransform>
86 <TransformGroup>
87 <ScaleTransform ScaleX="-1"/>
88 <SkewTransform/>
89 <RotateTransform/>
90 <TranslateTransform/>
91 </TransformGroup>
92 </Rectangle.RenderTransform>
93</Rectangle>
94<Button x:Name="CountSessions" Content="Show Active Connections" HorizontalAlignment="Left" VerticalAlignment="Top" Width="192" Margin="172,50,0,0"/>
95
96</Grid>
97</Window>
98"@
99 ####################End of XAML code##################################
100
101 #Load WPF into Hash Table.
102$syncHash = [hashtable]::Synchronized(@{})
103$reader=(New-Object System.Xml.XmlNodeReader $xaml)
104$syncHash.Window=[Windows.Markup.XamlReader]::Load( $reader )
105
106Function WebServer {
107 $syncHash.Host = $host
108 $Runspace = [runspacefactory]::CreateRunspace()
109 $Runspace.ApartmentState = "STA"
110 $Runspace.ThreadOptions = "ReuseThread"
111 $Runspace.Open()
112 $Runspace.SessionStateProxy.SetVariable("syncHash",$syncHash)
113
114
115 $code = {
116
117 <#
118.Synopsis
119Starts powershell webserver
120.Description
121Starts webserver as powershell process.
122Call of the root page (e.g. http://localhost:8080/) returns a powershell execution web form.
123Call of /script uploads a powershell script and executes it (as a function).
124Call of /log returns the webserver logs, /starttime the start time of the webserver, /time the current time.
125/download downloads and /upload uploads a file. /beep generates a sound and /quit or /exit stops the webserver.
126Any other call delivers the static content that fits to the path provided. If the static path is a directory,
127a file index.htm, index.html, default.htm or default.html in this directory is delivered if present.
128
129You may have to configure a firewall exception to allow access to the chosen port, e.g. with:
130 netsh advfirewall firewall add rule name="Powershell Webserver" dir=in action=allow protocol=TCP localport=8080
131
132After stopping the webserver you should remove the rule, e.g.:
133 netsh advfirewall firewall delete rule name="Powershell Webserver"
134.Parameter BINDING
135Binding of the webserver
136.Parameter BASEDIR
137Base directory for static content (default: the script's directory)
138.Inputs
139None
140.Outputs
141None
142.Example
143Start-Webserver.ps1
144
145Starts webserver with binding to http://localhost:8080/
146.Example
147Start-Webserver.ps1 "http://+:8080/"
148
149Starts webserver with binding to all IP addresses of the system.
150Administrative rights are necessary.
151.Example
152schtasks.exe /Create /TN "Powershell Webserver" /TR "powershell -file C:\Users\Markus\Documents\Start-WebServer.ps1 http://+:8080/" /SC ONSTART /RU SYSTEM /RL HIGHEST /F
153
154Starts powershell webserver as scheduled task as user local system every time the computer starts (when the
155correct path to the file Start-WebServer.ps1 is given).
156You can start the webserver task manually with
157 schtasks.exe /Run /TN "Powershell Webserver"
158Delete the webserver task with
159 schtasks.exe /Delete /TN "Powershell Webserver"
160Scheduled tasks are always running with low priority, so some functions might be slow.
161.Notes
162Version 1.1, 2017-11-23
163Author: Markus Scholtes
164#>
165Param([STRING]$BINDING = 'http://+:8080/', [STRING]$BASEDIR = "")
166
167# No adminstrative permissions are required for a binding to "localhost"
168# $BINDING = 'http://localhost:8080/'
169# Adminstrative permissions are required for a binding to network names or addresses.
170# + takes all requests to the port regardless of name or ip, * only requests that no other listener answers:
171# $BINDING = 'http://+:8080/'
172
173if ($BASEDIR -eq "")
174{ # retrieve script path as base path for static content
175 if ($MyInvocation.MyCommand.CommandType -eq "ExternalScript")
176 { $BASEDIR = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition }
177 else # compiled with PS2EXE:
178 { $BASEDIR = Split-Path -Parent -Path ([Environment]::GetCommandLineArgs()[0]) }
179}
180# convert to absolute path
181$BASEDIR = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($BASEDIR)
182
183# MIME hash table for static content
184$MIMEHASH = @{".avi"="video/x-msvideo"; ".crt"="application/x-x509-ca-cert"; ".css"="text/css"; ".der"="application/x-x509-ca-cert"; ".flv"="video/x-flv"; ".gif"="image/gif"; ".htm"="text/html"; ".html"="text/html"; ".ico"="image/x-icon"; ".jar"="application/java-archive"; ".jardiff"="application/x-java-archive-diff"; ".jpeg"="image/jpeg"; ".jpg"="image/jpeg"; ".js"="application/x-javascript"; ".mov"="video/quicktime"; ".mp3"="audio/mpeg"; ".mpeg"="video/mpeg"; ".mpg"="video/mpeg"; ".pdf"="application/pdf"; ".pem"="application/x-x509-ca-cert"; ".pl"="application/x-perl"; ".png"="image/png"; ".rss"="text/xml"; ".shtml"="text/html"; ".swf"="application/x-shockwave-flash"; ".txt"="text/plain"; ".war"="application/java-archive"; ".wmv"="video/x-ms-wmv"; ".xml"="text/xml"}
185
186# HTML answer templates for specific calls, placeholders !RESULT, !FORMFIELD, !PROMPT are allowed
187$HTMLRESPONSECONTENTS = @{
188 'GET /' = @"
189<html><body>
190 !HEADERLINE
191 <pre>!RESULT</pre>
192 <form method="GET" action="/">
193 <b>!PROMPT </b><input type="text" maxlength=255 size=80 name="command" value='!FORMFIELD'>
194 <input type="submit" name="button" value="Enter">
195 </form>
196</body></html>
197"@
198 'GET /script' = @"
199<html><body>
200 !HEADERLINE
201 <form method="POST" enctype="multipart/form-data" action="/script">
202 <p><b>Script to execute:</b><input type="file" name="filedata"></p>
203 <b>Parameters:</b><input type="text" maxlength=255 size=80 name="parameter">
204 <input type="submit" name="button" value="Execute">
205 </form>
206</body></html>
207"@
208 'GET /download' = @"
209<html><body>
210 !HEADERLINE
211 <pre>!RESULT</pre>
212 <form method="POST" action="/download">
213 <b>Path to file:</b><input type="text" maxlength=255 size=80 name="filepath" value='!FORMFIELD'>
214 <input type="submit" name="button" value="Download">
215 </form>
216</body></html>
217"@
218 'POST /download' = @"
219<html><body>
220 !HEADERLINE
221 <pre>!RESULT</pre>
222 <form method="POST" action="/download">
223 <b>Path to file:</b><input type="text" maxlength=255 size=80 name="filepath" value='!FORMFIELD'>
224 <input type="submit" name="button" value="Download">
225 </form>
226</body></html>
227"@
228 'GET /upload' = @"
229<html><body>
230 !HEADERLINE
231 <form method="POST" enctype="multipart/form-data" action="/upload">
232 <p><b>File to upload:</b><input type="file" name="filedata"></p>
233 <b>Path to store on webserver:</b><input type="text" maxlength=255 size=80 name="filepath">
234 <input type="submit" name="button" value="Upload">
235 </form>
236</body></html>
237"@
238 'POST /script' = "<html><body>!HEADERLINE<pre>!RESULT</pre></body></html>"
239 'POST /upload' = "<html><body>!HEADERLINE<pre>!RESULT</pre></body></html>"
240 'GET /exit' = "<html><body>Stopped powershell webserver</body></html>"
241 'GET /quit' = "<html><body>Stopped powershell webserver</body></html>"
242 'GET /log' = "<html><body>!HEADERLINELog of powershell webserver:<br /><pre>!RESULT</pre></body></html>"
243 'GET /starttime' = "<html><body>!HEADERLINEPowershell webserver started at $(Get-Date -Format s)</body></html>"
244 'GET /time' = "<html><body>!HEADERLINECurrent time: !RESULT</body></html>"
245 'GET /beep' = "<html><body>!HEADERLINEBEEP...</body></html>"
246}
247
248# Set navigation header line for all web pages
249$HEADERLINE = "<p><a href='/'>Command execution</a> <a href='/script'>Execute script</a> <a href='/download'>Download file</a> <a href='/upload'>Upload file</a> <a href='/log'>Web logs</a> <a href='/starttime'>Webserver start time</a> <a href='/time'>Current time</a> <a href='/beep'>Beep</a> <a href='/quit'>Stop webserver</a></p>"
250
251# Starting the powershell webserver
252"$(Get-Date -Format s) Starting powershell webserver..."
253$LISTENER = New-Object System.Net.HttpListener
254$LISTENER.Prefixes.Add($BINDING)
255$LISTENER.Start()
256$Error.Clear()
257
258try
259{
260 "$(Get-Date -Format s) Powershell webserver started."
261 $WEBLOG = "$(Get-Date -Format s) Powershell webserver started.`n"
262 while ($LISTENER.IsListening)
263 {
264 # analyze incoming request
265 $CONTEXT = $LISTENER.GetContext()
266 $REQUEST = $CONTEXT.Request
267 $RESPONSE = $CONTEXT.Response
268 $RESPONSEWRITTEN = $FALSE
269
270 # log to console
271 "$(Get-Date -Format s) $($REQUEST.RemoteEndPoint.Address.ToString()) $($REQUEST.httpMethod) $($REQUEST.Url.PathAndQuery)"
272 # and in log variable
273 $WEBLOG += "$(Get-Date -Format s) $($REQUEST.RemoteEndPoint.Address.ToString()) $($REQUEST.httpMethod) $($REQUEST.Url.PathAndQuery)`n"
274
275 # is there a fixed coding for the request?
276 $RECEIVED = '{0} {1}' -f $REQUEST.httpMethod, $REQUEST.Url.LocalPath
277 $HTMLRESPONSE = $HTMLRESPONSECONTENTS[$RECEIVED]
278 $RESULT = ''
279
280 # check for known commands
281 switch ($RECEIVED)
282 {
283 "GET /"
284 { # execute command
285 # retrieve GET query string
286 $FORMFIELD = ''
287 $FORMFIELD = [URI]::UnescapeDataString(($REQUEST.Url.Query -replace "\+"," "))
288 # remove fixed form fields out of query string
289 $FORMFIELD = $FORMFIELD -replace "\?command=","" -replace "\?button=enter","" -replace "&command=","" -replace "&button=enter",""
290 # when command is given...
291 if (![STRING]::IsNullOrEmpty($FORMFIELD))
292 {
293 try {
294 # ... execute command
295 $RESULT = Invoke-Expression -EA SilentlyContinue $FORMFIELD 2> $NULL | Out-String
296 }
297 catch {}
298 if ($Error.Count -gt 0)
299 { # retrieve error message on error
300 $RESULT += "`nError while executing '$FORMFIELD'`n`n"
301 $RESULT += $Error[0]
302 $Error.Clear()
303 }
304 }
305 # preset form value with command for the caller's convenience
306 $HTMLRESPONSE = $HTMLRESPONSE -replace '!FORMFIELD', $FORMFIELD
307 # insert powershell prompt to form
308 $PROMPT = "PS $PWD>"
309 $HTMLRESPONSE = $HTMLRESPONSE -replace '!PROMPT', $PROMPT
310 break
311 }
312
313 "GET /script"
314 { # present upload form, nothing to do here
315 break
316 }
317
318 "POST /script"
319 { # upload and execute script
320
321 # only if there is body data in the request
322 if ($REQUEST.HasEntityBody)
323 {
324 # set default message to error message (since we just stop processing on error)
325 $RESULT = "Received corrupt or incomplete form data"
326
327 # check content type
328 if ($REQUEST.ContentType)
329 {
330 # retrieve boundary marker for header separation
331 $BOUNDARY = $NULL
332 if ($REQUEST.ContentType -match "boundary=(.*);")
333 { $BOUNDARY = "--" + $MATCHES[1] }
334 else
335 { # marker might be at the end of the line
336 if ($REQUEST.ContentType -match "boundary=(.*)$")
337 { $BOUNDARY = "--" + $MATCHES[1] }
338 }
339
340 if ($BOUNDARY)
341 { # only if header separator was found
342
343 # read complete header (inkl. file data) into string
344 $READER = New-Object System.IO.StreamReader($REQUEST.InputStream, $REQUEST.ContentEncoding)
345 $DATA = $READER.ReadToEnd()
346 $READER.Close()
347 $REQUEST.InputStream.Close()
348
349 $PARAMETERS = ""
350 $SOURCENAME = ""
351
352 # separate headers by boundary string
353 $DATA -replace "$BOUNDARY--\r\n", "$BOUNDARY`r`n--" -split "$BOUNDARY\r\n" | % {
354 # omit leading empty header and end marker header
355 if (($_ -ne "") -and ($_ -ne "--"))
356 {
357 # only if well defined header (separation between meta data and data)
358 if ($_.IndexOf("`r`n`r`n") -gt 0)
359 {
360 # header data before two CRs is meta data
361 # first look for the file in header "filedata"
362 if ($_.Substring(0, $_.IndexOf("`r`n`r`n")) -match "Content-Disposition: form-data; name=(.*);")
363 {
364 $HEADERNAME = $MATCHES[1] -replace '\"'
365 # headername "filedata"?
366 if ($HEADERNAME -eq "filedata")
367 { # yes, look for source filename
368 if ($_.Substring(0, $_.IndexOf("`r`n`r`n")) -match "filename=(.*)")
369 { # source filename found
370 $SOURCENAME = $MATCHES[1] -replace "`r`n$" -replace "`r$" -replace '\"'
371 # store content of file in variable
372 $FILEDATA = $_.Substring($_.IndexOf("`r`n`r`n") + 4) -replace "`r`n$"
373 }
374 }
375 }
376 else
377 { # look for other headers (we need "parameter")
378 if ($_.Substring(0, $_.IndexOf("`r`n`r`n")) -match "Content-Disposition: form-data; name=(.*)")
379 { # header found
380 $HEADERNAME = $MATCHES[1] -replace '\"'
381 # headername "parameter"?
382 if ($HEADERNAME -eq "parameter")
383 { # yes, look for paramaters
384 $PARAMETERS = $_.Substring($_.IndexOf("`r`n`r`n") + 4) -replace "`r`n$" -replace "`r$"
385 }
386 }
387 }
388 }
389 }
390 }
391
392 if ($SOURCENAME -ne "")
393 { # execute only if a source file exists
394
395 $EXECUTE = "function Powershell-WebServer-Func {`n" + $FILEDATA + "`n}`nPowershell-WebServer-Func " + $PARAMETERS
396 try {
397 # ... execute script
398 $RESULT = Invoke-Expression -EA SilentlyContinue $EXECUTE 2> $NULL | Out-String
399 }
400 catch {}
401 if ($Error.Count -gt 0)
402 { # retrieve error message on error
403 $RESULT += "`nError while executing script $SOURCENAME`n`n"
404 $RESULT += $Error[0]
405 $Error.Clear()
406 }
407 }
408 else
409 {
410 $RESULT = "No file data received"
411 }
412 }
413 }
414 }
415 else
416 {
417 $RESULT = "No client data received"
418 }
419 break
420 }
421
422 { $_ -like "* /download" } # GET or POST method are allowed for download page
423 { # download file
424
425 # is POST data in the request?
426 if ($REQUEST.HasEntityBody)
427 { # POST request
428 # read complete header into string
429 $READER = New-Object System.IO.StreamReader($REQUEST.InputStream, $REQUEST.ContentEncoding)
430 $DATA = $READER.ReadToEnd()
431 $READER.Close()
432 $REQUEST.InputStream.Close()
433
434 # get headers into hash table
435 $HEADER = @{}
436 $DATA.Split('&') | % { $HEADER.Add([URI]::UnescapeDataString(($_.Split('=')[0] -replace "\+"," ")), [URI]::UnescapeDataString(($_.Split('=')[1] -replace "\+"," "))) }
437
438 # read header 'filepath'
439 $FORMFIELD = $HEADER.Item('filepath')
440 # remove leading and trailing double quotes since Test-Path does not like them
441 $FORMFIELD = $FORMFIELD -replace "^`"","" -replace "`"$",""
442 }
443 else
444 { # GET request
445
446 # retrieve GET query string
447 $FORMFIELD = ''
448 $FORMFIELD = [URI]::UnescapeDataString(($REQUEST.Url.Query -replace "\+"," "))
449 # remove fixed form fields out of query string
450 $FORMFIELD = $FORMFIELD -replace "\?filepath=","" -replace "\?button=download","" -replace "&filepath=","" -replace "&button=download",""
451 # remove leading and trailing double quotes since Test-Path does not like them
452 $FORMFIELD = $FORMFIELD -replace "^`"","" -replace "`"$",""
453 }
454
455 # when path is given...
456 if (![STRING]::IsNullOrEmpty($FORMFIELD))
457 { # check if file exists
458 if (Test-Path $FORMFIELD -PathType Leaf)
459 {
460 try {
461 # ... download file
462 $BUFFER = [System.IO.File]::ReadAllBytes($FORMFIELD)
463 $RESPONSE.ContentLength64 = $BUFFER.Length
464 $RESPONSE.SendChunked = $FALSE
465 $RESPONSE.ContentType = "application/octet-stream"
466 $FILENAME = Split-Path -Leaf $FORMFIELD
467 $RESPONSE.AddHeader("Content-Disposition", "attachment; filename=$FILENAME")
468 $RESPONSE.AddHeader("Last-Modified", [IO.File]::GetLastWriteTime($FORMFIELD).ToString('r'))
469 $RESPONSE.AddHeader("Server", "Powershell Webserver/1.1 on ")
470 $RESPONSE.OutputStream.Write($BUFFER, 0, $BUFFER.Length)
471 # mark response as already given
472 $RESPONSEWRITTEN = $TRUE
473 }
474 catch {}
475 if ($Error.Count -gt 0)
476 { # retrieve error message on error
477 $RESULT += "`nError while downloading '$FORMFIELD'`n`n"
478 $RESULT += $Error[0]
479 $Error.Clear()
480 }
481 }
482 else
483 {
484 # ... file not found
485 $RESULT = "File $FORMFIELD not found"
486 }
487 }
488 # preset form value with file path for the caller's convenience
489 $HTMLRESPONSE = $HTMLRESPONSE -replace '!FORMFIELD', $FORMFIELD
490 break
491 }
492
493 "GET /upload"
494 { # present upload form, nothing to do here
495 break
496 }
497
498 "POST /upload"
499 { # upload file
500
501 # only if there is body data in the request
502 if ($REQUEST.HasEntityBody)
503 {
504 # set default message to error message (since we just stop processing on error)
505 $RESULT = "Received corrupt or incomplete form data"
506
507 # check content type
508 if ($REQUEST.ContentType)
509 {
510 # retrieve boundary marker for header separation
511 $BOUNDARY = $NULL
512 if ($REQUEST.ContentType -match "boundary=(.*);")
513 { $BOUNDARY = "--" + $MATCHES[1] }
514 else
515 { # marker might be at the end of the line
516 if ($REQUEST.ContentType -match "boundary=(.*)$")
517 { $BOUNDARY = "--" + $MATCHES[1] }
518 }
519
520 if ($BOUNDARY)
521 { # only if header separator was found
522
523 # read complete header (inkl. file data) into string
524 $READER = New-Object System.IO.StreamReader($REQUEST.InputStream, $REQUEST.ContentEncoding)
525 $DATA = $READER.ReadToEnd()
526 $READER.Close()
527 $REQUEST.InputStream.Close()
528
529 # variables for filenames
530 $FILENAME = ""
531 $SOURCENAME = ""
532
533 # separate headers by boundary string
534 $DATA -replace "$BOUNDARY--\r\n", "$BOUNDARY`r`n--" -split "$BOUNDARY\r\n" | % {
535 # omit leading empty header and end marker header
536 if (($_ -ne "") -and ($_ -ne "--"))
537 {
538 # only if well defined header (seperation between meta data and data)
539 if ($_.IndexOf("`r`n`r`n") -gt 0)
540 {
541 # header data before two CRs is meta data
542 # first look for the file in header "filedata"
543 if ($_.Substring(0, $_.IndexOf("`r`n`r`n")) -match "Content-Disposition: form-data; name=(.*);")
544 {
545 $HEADERNAME = $MATCHES[1] -replace '\"'
546 # headername "filedata"?
547 if ($HEADERNAME -eq "filedata")
548 { # yes, look for source filename
549 if ($_.Substring(0, $_.IndexOf("`r`n`r`n")) -match "filename=(.*)")
550 { # source filename found
551 $SOURCENAME = $MATCHES[1] -replace "`r`n$" -replace "`r$" -replace '\"'
552 # store content of file in variable
553 $FILEDATA = $_.Substring($_.IndexOf("`r`n`r`n") + 4) -replace "`r`n$"
554 }
555 }
556 }
557 else
558 { # look for other headers (we need "filepath" to know where to store the file)
559 if ($_.Substring(0, $_.IndexOf("`r`n`r`n")) -match "Content-Disposition: form-data; name=(.*)")
560 { # header found
561 $HEADERNAME = $MATCHES[1] -replace '\"'
562 # headername "filepath"?
563 if ($HEADERNAME -eq "filepath")
564 { # yes, look for target filename
565 $FILENAME = $_.Substring($_.IndexOf("`r`n`r`n") + 4) -replace "`r`n$" -replace "`r$" -replace '\"'
566 }
567 }
568 }
569 }
570 }
571 }
572
573 if ($FILENAME -ne "")
574 { # upload only if a targetname is given
575 if ($SOURCENAME -ne "")
576 { # only upload if source file exists
577
578 # check or construct a valid filename to store
579 $TARGETNAME = ""
580 # if filename is a container name, add source filename to it
581 if (Test-Path $FILENAME -PathType Container)
582 {
583 $TARGETNAME = Join-Path $FILENAME -ChildPath $(Split-Path $SOURCENAME -Leaf)
584 } else {
585 # try name in the header
586 $TARGETNAME = $FILENAME
587 }
588
589 try {
590 # ... save file with the same encoding as received
591 [IO.File]::WriteAllText($TARGETNAME, $FILEDATA, $REQUEST.ContentEncoding)
592 }
593 catch {}
594 if ($Error.Count -gt 0)
595 { # retrieve error message on error
596 $RESULT += "`nError saving '$TARGETNAME'`n`n"
597 $RESULT += $Error[0]
598 $Error.Clear()
599 }
600 else
601 { # success
602 $RESULT = "File $SOURCENAME successfully uploaded as $TARGETNAME"
603 }
604 }
605 else
606 {
607 $RESULT = "No file data received"
608 }
609 }
610 else
611 {
612 $RESULT = "Missing target file name"
613 }
614 }
615 }
616 }
617 else
618 {
619 $RESULT = "No client data received"
620 }
621 break
622 }
623
624 "GET /log"
625 { # return the webserver log (stored in log variable)
626 $RESULT = $WEBLOG
627 break
628 }
629
630 "GET /time"
631 { # return current time
632 $RESULT = Get-Date -Format s
633 break
634 }
635
636 "GET /starttime"
637 { # return start time of the powershell webserver (already contained in $HTMLRESPONSE, nothing to do here)
638 break
639 }
640
641 "GET /beep"
642 { # Beep
643 [CONSOLE]::beep(800, 300) # or "`a" or [char]7
644 break
645 }
646
647 "GET /quit"
648 { # stop powershell webserver, nothing to do here
649 break
650 }
651
652 "GET /exit"
653 { # stop powershell webserver, nothing to do here
654 break
655 }
656
657 default
658 { # unknown command, check if path to file
659
660 # create physical path based upon the base dir and url
661 $CHECKDIR = $BASEDIR.TrimEnd("/\") + $REQUEST.Url.LocalPath
662 $CHECKFILE = ""
663 if (Test-Path $CHECKDIR -PathType Container)
664 { # physical path is a directory
665 $IDXLIST = "/index.htm", "/index.html", "/default.htm", "/default.html"
666 foreach ($IDXNAME in $IDXLIST)
667 { # check if an index file is present
668 $CHECKFILE = $CHECKDIR.TrimEnd("/\") + $IDXNAME
669 if (Test-Path $CHECKFILE -PathType Leaf)
670 { # index file found, path now in $CHECKFILE
671 break
672 }
673 $CHECKFILE = ""
674 }
675 }
676 else
677 { # no directory, check for file
678 if (Test-Path $CHECKDIR -PathType Leaf)
679 { # file found, path now in $CHECKFILE
680 $CHECKFILE = $CHECKDIR
681 }
682 }
683
684 if ($CHECKFILE -ne "")
685 { # static content available
686 try {
687 # ... serve static content
688 $BUFFER = [System.IO.File]::ReadAllBytes($CHECKFILE)
689 $RESPONSE.ContentLength64 = $BUFFER.Length
690 $RESPONSE.SendChunked = $FALSE
691 $EXTENSION = [IO.Path]::GetExtension($CHECKFILE)
692 if ($MIMEHASH.ContainsKey($EXTENSION))
693 { # known mime type for this file's extension available
694 $RESPONSE.ContentType = $MIMEHASH.Item($EXTENSION)
695 }
696 else
697 { # no, serve as binary download
698 $RESPONSE.ContentType = "application/octet-stream"
699 $FILENAME = Split-Path -Leaf $CHECKFILE
700 $RESPONSE.AddHeader("Content-Disposition", "attachment; filename=$FILENAME")
701 }
702 $RESPONSE.AddHeader("Last-Modified", [IO.File]::GetLastWriteTime($CHECKFILE).ToString('r'))
703 $RESPONSE.AddHeader("Server", "Powershell Webserver/1.1 on ")
704 $RESPONSE.OutputStream.Write($BUFFER, 0, $BUFFER.Length)
705 # mark response as already given
706 $RESPONSEWRITTEN = $TRUE
707 }
708 catch {}
709 if ($Error.Count -gt 0)
710 { # retrieve error message on error
711 $RESULT += "`nError while downloading '$CHECKFILE'`n`n"
712 $RESULT += $Error[0]
713 $Error.Clear()
714 }
715 }
716 else
717 { # no file to serve found, return error
718 $RESPONSE.StatusCode = 404
719 $HTMLRESPONSE = '<html><body>Page not found</body></html>'
720 }
721 }
722
723 }
724
725 # only send response if not already done
726 if (!$RESPONSEWRITTEN)
727 {
728 # insert header line string into HTML template
729 $HTMLRESPONSE = $HTMLRESPONSE -replace '!HEADERLINE', $HEADERLINE
730
731 # insert result string into HTML template
732 $HTMLRESPONSE = $HTMLRESPONSE -replace '!RESULT', $RESULT
733
734 # return HTML answer to caller
735 $BUFFER = [Text.Encoding]::UTF8.GetBytes($HTMLRESPONSE)
736 $RESPONSE.ContentLength64 = $BUFFER.Length
737 $RESPONSE.AddHeader("Last-Modified", [DATETIME]::Now.ToString('r'))
738 $RESPONSE.AddHeader("Server", "Powershell Webserver/1.1 on ")
739 $RESPONSE.OutputStream.Write($BUFFER, 0, $BUFFER.Length)
740 }
741
742 # and finish answer to client
743 $RESPONSE.Close()
744
745 # received command to stop webserver?
746 if ($RECEIVED -eq 'GET /exit' -or $RECEIVED -eq 'GET /quit')
747 { # then break out of while loop
748 "$(Get-Date -Format s) Stopping powershell webserver..."
749 break;
750 }
751 }
752}
753finally
754{
755 # Stop powershell webserver
756 $LISTENER.Stop()
757 $LISTENER.Close()
758 "$(Get-Date -Format s) Powershell webserver stopped."
759}
760
761
762 }#End of code block.
763$PSinstance = [powershell]::Create().AddScript($Code)
764$PSinstance.Runspace = $Runspace
765$job = $PSinstance.BeginInvoke()
766
767}#End of WebServer Function
768
769#Functions are here so I can cleanly put them into my button clicks for later.
770Function StartCaching {
771$syncHash.Host = $host
772$Runspace = [runspacefactory]::CreateRunspace()
773$Runspace.ApartmentState = "STA"
774$Runspace.ThreadOptions = "ReuseThread"
775$Runspace.Open()
776$Runspace.SessionStateProxy.SetVariable("syncHash",$syncHash)
777
778
779$code = {
780 #$syncHash.Window.Dispatcher.invoke([action]{})
781 $syncHash.TextBlock_SyncStatus.Dispatcher.invoke([action]{
782 If ((Get-Service -Name Server).Status -Eq "Running"){$syncHash.TextBlock_SyncStatus.text = "Halting any conflicting services."}
783 $syncHash.Rectangle_Status.Fill = "Red"
784 })
785 $syncHash.TextBlock_SyncStatus.Dispatcher.invoke([action]{
786 If ((Get-Service -Name Server).Status -Eq "Running") {Stop-Service Server}
787 Start-Sleep 1
788 })
789 $syncHash.TextBlock_SyncStatus.Dispatcher.invoke([action]{
790 $syncHash.TextBlock_SyncStatus.text = "Syncing Files"
791 $syncHash.Rectangle_Status.Fill = "Yellow"
792 })
793
794 #Image Dependancies
795 robocopy "\\sadmzpr04.admin.com\Wim\OEM" "C:\Wim\OEM" /MIR /R:0 /w:0
796 robocopy "\\sadmzpr04.admin.com\Wim\Images" "C:\Wim\Images" /MIR /R:0 /w:0
797 robocopy "\\sadmzpr04.admin.com\Production Base Apps\Production" "C:\Production Base Apps\Production" /MIR /R:0 /w:0
798 #Drivers
799 robocopy "\\sadmzpr04.admin.com\Wim\Drivers\Windows10\HPStreamG2" "C:\Wim\Drivers\Windows10\HPStreamG2" /MIR /R:0 /w:0
800 robocopy "\\sadmzpr04.admin.com\Wim\Drivers\Windows10\HPStreamG3" "C:\Wim\Drivers\Windows10\HPStreamG3" /MIR /R:0 /w:0
801 robocopy "\\sadmzpr04.admin.com\Wim\Drivers\Windows10\DellLatitude3390" "C:\Wim\Drivers\Windows10\DellLatitude3390" /MIR /R:0 /w:0
802 robocopy "\\sadmzpr04.admin.com\Wim\Drivers\Windows10\HPProBook640G3" "C:\Wim\Drivers\Windows10\HPProBook640G3" /MIR /R:0 /w:0
803 robocopy "\\sadmzpr04.admin.com\Wim\Drivers\Windows10\HPProbook645G2" "C:\Wim\Drivers\Windows10\HPProbook645G2" /MIR /R:0 /w:0
804 robocopy "\\sadmzpr04.admin.com\Wim\Drivers\Windows10\HPProbook645G2" "C:\Wim\Drivers\Windows10\HPProbook645G2" /MIR /R:0 /w:0
805 robocopy "\\sadmzpr04.admin.com\Wim\Drivers\Windows10\HPProBook11G2" "C:\Wim\Drivers\Windows10\HPProBook11G2" /MIR /R:0 /w:0
806 robocopy "\\sadmzpr04.admin.com\Wim\Drivers\Windows10\DellLatitude3189" "C:\Wim\Drivers\Windows10\DellLatitude3189" /MIR /R:0 /w:0
807
808 $syncHash.TextBlock_SyncStatus.Dispatcher.invoke([action]{
809 $syncHash.TextBlock_SyncStatus.text = "Syncronization complete, starting Satelite Server Services."
810 })
811
812 Start-Service Server
813 Start-Sleep 4
814
815 If ((Get-Service -Name Server).Status -Eq "Running"){
816 $syncHash.TextBlock_SyncStatus.Dispatcher.invoke([action]{
817 $syncHash.TextBlock_SyncStatus.text = "Services started sucessfully. This device is now a functioning imaging caching device."
818 })
819 $syncHash.Rectangle_Status.Dispatcher.invoke([action]{
820 $syncHash.Rectangle_Status.Fill = "Green"
821 })
822
823 }
824 ElseIf ((Get-Service -Name Server).Status -Ne "Running"){
825 $syncHash.TextBlock_SyncStatus.Dispatcher.invoke([action]{
826 $syncHash.TextBlock_SyncStatus.text = "I have no idea what went wrong, but I couldn't start the server services.";
827 })
828 $syncHash.Rectangle_Status.Dispatcher.invoke([action]{
829 $syncHash.Rectangle_Status.Fill = "Red"
830 })
831 }
832 Else {
833 $syncHash.TextBlock_SyncStatus.Dispatcher.invoke([action]{
834 $syncHash.TextBlock_SyncStatus.text = "The satelite services could not be started. This machine is not currently working as a caching device."
835 })
836 $syncHash.Rectangle_Status.Dispatcher.invoke([action]{
837 $syncHash.Rectangle_Status.Fill = "Red"
838 })
839 }
840
841}#end of code block.
842$PSinstance = [powershell]::Create().AddScript($Code)
843$PSinstance.Runspace = $Runspace
844$job = $PSinstance.BeginInvoke()
845}
846
847
848Function ShutDown {
849 $syncHash.Host = $host
850 $Runspace = [runspacefactory]::CreateRunspace()
851 $Runspace.ApartmentState = "STA"
852 $Runspace.ThreadOptions = "ReuseThread"
853 $Runspace.Open()
854 $Runspace.SessionStateProxy.SetVariable("syncHash",$syncHash)
855
856
857 $code = {
858 #$syncHash.Window.Dispatcher.invoke([action]{})
859 $syncHash.Window.Dispatcher.invoke([action]{
860 taskkill /im powershell.exe /f
861 })
862 }
863 $PSinstance = [powershell]::Create().AddScript($Code)
864 $PSinstance.Runspace = $Runspace
865 $job = $PSinstance.BeginInvoke()
866 }
867
868 Function CountConnections {
869
870$runspace = [runspacefactory]::CreateRunspace()
871$powerShell = [powershell]::Create()
872$powerShell.runspace = $runspace
873$runspace.Open()
874$runspace.SessionStateProxy.SetVariable("syncHash",$syncHash)
875
876
877$code = {
878 Improt-Module SMBShare
879 Do {
880 $smb = Get-SMBSession | Where-Object {$_.ClientComputerName -Like "10.*"}
881 $Global:SessionCount = ($smb.clientcomputername).count
882 #Check if the variable is NULL.
883 IF (!($Global:SessionCount)) {$Global:SessionCount = "0"}
884 #Change content of TextBlock to the active number of connections
885 $syncHash.TextBlock_ActiveConnections.Dispatcher.invoke([action]{
886 $syncHash.TextBlock_ActiveConnections.Text = "Number of clients connected:
887 $Global:SessionCount (Maximum of 20)"
888 })
889 Start-Sleep3
890 }Until ($Stop -eq "2")
891}
892$PSinstance = [powershell]::Create().AddScript($Code)
893$PSinstance.Runspace = $Runspace
894$job = $PSinstance.BeginInvoke()
895 }
896
897#########################Map WPF controls into variable contained int he hash tables.
898
899#Easy copy and paste template for future mappings.
900#$syncHash. = $syncHash.Window.FindName("")
901#Buttons
902$syncHash.Button_ShutDown = $syncHash.Window.FindName("Button_ShutDown")
903$syncHash.Button_StartCachingServices = $syncHash.Window.FindName("Button_StartCachingServices")
904$syncHash.CountSessions = $syncHash.Window.FindName("CountSessions")
905
906#TextBlocks
907$syncHash.TextBlock_SyncStatus = $syncHash.Window.FindName("TextBlock_SyncStatus")
908$syncHash.TextBlock_ActiveConnections = $syncHash.Window.FindName("TextBlock_ActiveConnections")
909
910#Rectangles
911$syncHash.Rectangle_Status = $syncHash.Window.FindName("Rectangle_Status")
912
913#Button Click Actions.
914$syncHash.Button_ShutDown.Add_Click({ShutDown})
915$syncHash.Button_StartCachingServices.Add_Click({
916 StartCaching
917
918 WebServer
919})
920$syncHash.CountSessions.Add_Click({CountConnections})
921
922
923#Launch GUI
924$syncHash.Window.ShowDialog()
925$Runspace.Close()
926$Runspace.Dispose()
927
928
929
930
931}
932
933$PSinstance1 = [powershell]::Create().AddScript($Code)
934$PSinstance1.Runspace = $Runspace
935$job = $PSinstance1.BeginInvoke()
936
937
938
939Start-Sleep 10