7DaysToDieManager.ps1

# Copyright 2019 - Sean Davis
# GNU General Public License v3

function Start-7DaysToDieManager {
 
<#
    .SYNOPSIS
      
        Powershell Web Server to serve HTML and Powershell web contents.
  
    .DESCRIPTION
      
        Listens a port to serve web content. Supports HTML and Powershell.
     
    .PARAMETER WhatIf
      
        Display what would happen if you would run the function with given parameters.
     
    .PARAMETER Confirm
      
        Prompts for confirmation for each operation. Allow user to specify Yes/No to all option to stop prompting.
     
    .EXAMPLE
      
        Start-7DaysToDieManager -IP 127.0.0.1 -Port 8080
         
    .EXAMPLE
      
        Start-7DaysToDieManager -Hostname "7DaysToDieManager.net" -Port 8080
         
    .EXAMPLE
      
        Start-7DaysToDieManager -Hostname "7DaysToDieManager.net" -Port 8080 -asJob
         
    .EXAMPLE
      
        Start-7DaysToDieManager -Hostname "7DaysToDieManager.net" -Port 8080 -SSL -SSLPort 8443 -asJob
         
    .EXAMPLE
      
        Start-7DaysToDieManager -Hostname "7DaysToDieManager.net" -Port 8080 -SSL -SSLIP "127.0.0.1" -SSLPort 8443 -asJob
         
    .EXAMPLE
      
        Start-7DaysToDieManager -Hostname "7DaysToDieManager.net" -Port 8080 -DebugMode
         
    .EXAMPLE
      
        Start-7DaysToDieManager -Hostname "7DaysToDieManager.net,www.7DaysToDieManager.net" -Port 8080
         
    .EXAMPLE
      
        Start-7DaysToDieManager -Hostname "7DaysToDieManager.net,www.7DaysToDieManager.net" -Port 8080 -HomeDirectory "C:\inetpub\wwwroot"
         
    .EXAMPLE
      
        Start-7DaysToDieManager -Hostname "7DaysToDieManager.net,www.7DaysToDieManager.net" -Port 8080 -HomeDirectory "C:\inetpub\wwwroot" -LogDirectory "C:\inetpub\wwwroot"
         
    .EXAMPLE
      
        Start-7DaysToDieManager -Hostname "7DaysToDieManager.net" -Port 8080 -CustomConfig "C:\inetpub\config.ps1" -CustomJob "C:\inetpub\job.ps1"
 
    .INPUTS
     
        None
  
    .OUTPUTS
  
        None
     
    .NOTES
     
        Author: Sean Davis
        Website: http://www.imseandavis.com
        Email: imseandavis@gmail.com
        Date created: 16-Oct-2019
        Last modified: 16-Oct-2019
        Version: 1.0
  
    .LINK
     
        http://7DaysToDieManager.seanasaservice.com
         
#>

 
[CmdletBinding(SupportsShouldProcess = $true)]
param (

    # Hostname
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'IP Address or Hostname')]
    [Alias('IP')]
    [string]$Hostname,
    
    # Port Number
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Port Number')]
    [string]$Port,
    
    # SSL IP Address
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'SSL IP Address')]
    [string]$SSLIP,
    
    # SSL Port Number
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'SSL Port Number')]
    [string]$SSLPort,
    
    # SSL Port Number
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'SSL Friendly Name. Example: 7DaysToDieManager.net')]
    [string]$SSLName,
    
    # Home Directory
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Home Directory. Example: C:\inetpub\wwwroot')]
    [string]$HomeDirectory,
    
    # Log Directory
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Log Directory. Example: C:\inetpub\wwwroot')]
    [string]$LogDirectory,

    # Custom Config Path
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Custom Config Path. Example: C:\inetpub\config.ps1')]
    [string]$CustomConfig,

    # Custom Child Config Path
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Custom Child Config Path. Example: C:\inetpub\childconfig.ps1')]
    [string]$CustomChildConfig,

    # Custom Job Path
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Custom Job Path. Example: C:\inetpub\jobs.ps1')]
    [string]$CustomJob,
    
    # Custom Job Schedule
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Custom Job Schedule. Example: 1, 5, 10, 20, 30, 60')]
        [ValidateSet("1","5","10","20","30","60")] 
    [string]$CustomJobSchedule = "5",
    
    # Background Job ID
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Background Job ID. Example: 52341')]
    [string]$JobID,

    # Background Job Username
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Background Job Username. Example: CONTOSO\Administrator')]
    [string]$JobUsername,
    
    # Background Job User Password
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Background Job User Password. Example: P@ssw0rd1')]
    [string]$JobPassword,
    
    # Background Job Credentials
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Run Background Job as Different User')]
    [switch]$JobCredentials = $false,
    
    # Enable SSL
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Enable SSL')]
    [switch]$SSL = $false,

    # Debug Mode
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Debug Mode')]
    [switch]$DebugMode = $false,
    
    # Background Job
    [Parameter(
        Mandatory = $false,
        HelpMessage = 'Run As Background Job')]
    [switch]$asJob = $false
)
    
    # Enable Debug Mode
    if ($DebugMode)
    {
        $DebugPreference = "Continue"
    }
    else
    {
        $ErrorActionPreference = "silentlycontinue"
    }

    # Get 7DTDSM Server Path
    $7DaysToDieManagerPath = "C:\Program Files\7DaysToDieManager"
    
    # Get 7DTDSM Server Module Path
    $7DTDSMModulePath = "C:\Program Files\7DaysToDieManager\modules"
    
    # Test 7DTDSM Server Module Path
    $7DTDSMModulePathTest = Test-Path $7DTDSMModulePath
    
    if (!$7DTDSMModulePathTest)
    {
        $ModulePaths = ($env:PSModulePath).Split(";")
        
        # Test Module Paths
        Foreach ($ModulePath in $ModulePaths)
        {
            $ModulePath = "$ModulePath\7DaysToDieManager"
            $ModulePath = $ModulePath.Replace("\\","\")
            $7DTDSMModulePathTest = Test-Path $ModulePath
            if ($7DTDSMModulePathTest)
            {
                $7DTDSMModulePath = $ModulePath
            }
        }
    }
    
    if (!$7DTDSMModulePathTest)
    {
        Write-Warning "Could not detect 7DTDSM Server Module Path."
        Write-Warning "Aborting.."
        
        $ResultCode = "-1"
        $ResultMessage = "Could not detect 7DTDSM Server Module Path."
    }
    else
    {
        # Import Functions
        . $7DTDSMModulePath\modules\functions.ps1
    }
    
    # Background Job Control
    if ($asJob -and $ResultCode -ne "-1")
    {
        if ($JobCredentials)
        {
            Write-Host " "
            Write-Host "Plase specify user credentials for 7DTDSM Server background job."
            Write-Host " "
            $JobUsername = Read-Host -Prompt "Username"
            $JobSecurePassword = Read-Host -Prompt "Password" -AsSecureString            
            $JobSecureCredentials = New-Object -Typename System.Management.Automation.PSCredential -ArgumentList $JobUsername,$JobSecurePassword
            $JobPassword = $JobSecureCredentials.GetNetworkCredential().Password
        }
    }
    
    # Background Job ID
    if ($JobID -and $ResultCode -ne "-1")
    {
        $JobIDPath = "$7DaysToDieManagerPath\jobs\job-$JobID.txt"
        $TestJobID = Test-Path $JobIDPath
        if ($JobIDPath)
        {
            $JobIDContent = Get-Content $JobIDPath
            $Hostname = $JobIDContent.Split(";")[0]
            $Port = $JobIDContent.Split(";")[1]
            $SSLIP = $JobIDContent.Split(";")[2]
            $SSLPort = $JobIDContent.Split(";")[3]
            $SSLName = $JobIDContent.Split(";")[4]
            $HomeDirectory = $JobIDContent.Split(";")[5]
            $LogDirectory = $JobIDContent.Split(";")[6]
            $CustomConfig = $JobIDContent.Split(";")[7]
            $CustomChildConfig = $JobIDContent.Split(";")[8]
            $CustomJob = $JobIDContent.Split(";")[9]
        }
        else
        {
            Write-Warning "Job ID is not exist."
            Write-Warning "Aborting.."

            $ResultCode = "-1"
            $ResultMessage = "Job ID is not exist."
        }
    }
    
    if ($ResultCode -ne "-1")
    {    
        # Get Home and Log Directories
        if (!$HomeDirectory) { $HomeDirectory = "$7DaysToDieManagerPath\webroot\http" }
        if (!$LogDirectory) { $LogDirectory = "$7DaysToDieManagerPath\webroot\logs" }

        # Admin Privileges Verification
        . $7DTDSMModulePath\modules\adminverification.ps1
        
        # Server IP Verification
        . $7DTDSMModulePath\modules\ipverification.ps1
        
        # PHP Encoding Module
        . $7DTDSMModulePath\modules\phpencoding.ps1
        
        # Break Script If Something's Wrong
        if ($ShouldProcess -eq $false)
        {
            $ResultCode = "-1"
            $ResultMessage = "Please check module output."
        }
    }
    
    if ($ResultCode -ne "-1")
    {    
        # Enable Background Job
        if ($asJob)
        {    
            if (!$Hostname)
            {
                $Hostname = "+"
                $TaskHostname = "localhost"
            }
            else
            {
                $TaskHostname = $Hostname.Split(",")[0]
            }
            
            if (!$Port)
            {
                $Port = "8080"
                $TaskPort = "8080"
            }
            else
            {
                $TaskPort = $Port.Split(",")[0]
            }
            
            if ($SSL)
            {
                if (!$SSLIP) 
                {
                    $SSLIP = "127.0.0.1"
                    
                    if (!$SSLPort)
                    {
                        $SSLPort = "8443"
                    }
                }
            }
            
            $CheckTask = schtasks.exe | where {$_ -like "7DaysToDieManager-$TaskHostname-$TaskPort*"}
            if ($CheckTask)
            {
                Write-Warning "This job is already exist. You should run it from Scheduled Jobs."
                Write-Warning "Aborting.."
                
                $ResultCode = "-1"
                $ResultMessage = "This job is already exist. You should run it from Scheduled Jobs."
            }
            else
            {
                # Prepare Job Information
                $TaskID = Get-Random -Maximum 10000
                $TaskName = "7DaysToDieManager-$TaskHostname-$TaskPort-$TaskID"
                $CreateJobIDPath = $7DaysToDieManagerPath + "\jobs\job-" + $TaskID + ".txt"
                $CreateJobIDValue = $Hostname + ";" + $Port + ";" + $SSLIP + ";" + $SSLPort + ";" + $SSLName + ";" + $HomeDirectory + ";" + $LogDirectory + ";" + $CustomConfig + ";" + $CustomChildConfig + ";" + $CustomJob
                $CreateJobID = Add-Content -Path $CreateJobIDPath -Value $CreateJobIDValue
                
                # Create Scheduled Jobs
                $CreateTask = schtasks /create /tn "$TaskName" /xml "$7DaysToDieManagerPath\jobs\template.xml" /ru SYSTEM
                $ChangeTaskProcess = $true
                while ($ChangeTaskProcess)
                {
                    if ($SSL)
                    {
                        $ChangeTask = schtasks /change /tn "$TaskName" /tr "Powershell -Command &{Import-Module 7DaysToDieManager; Start-7DaysToDieManager -SSL -JobID $TaskID}" /rl highest
                    }
                    else
                    {
                        $ChangeTask = schtasks /change /tn "$TaskName" /tr "Powershell -Command &{Import-Module 7DaysToDieManager; Start-7DaysToDieManager -JobID $TaskID}" /rl highest
                    }
                    
                    if ($ChangeTask)
                    {
                        $ChangeTaskProcess = $false
                    }
                }
                
                if ($JobUsername -and $JobPassword)
                {
                    $ChangeTaskProcess = $true
                    while ($ChangeTaskProcess)
                    {
                        $ChangeTask = schtasks /tn "$TaskName" /Change /RU "$JobUsername" /RP "$JobPassword"
                        
                        if ($ChangeTask)
                        {
                            $ChangeTaskProcess = $false
                        }
                    }
                }
                
                # Start Background Job
                $RunTask = schtasks /run /tn "$TaskName"
                
                # 7DTDSM Server Welcome Banner
                Get-7DTDSMWelcomeBanner -Hostname $Hostname -Port $Port -SSL $SSL -SSLIP $SSLIP -SSLPort $SSLPort -DebugMode $DebugMode
            }
        }
        else
        {
            # 7DTDSM Server Scheduled Background Jobs
            $7DTDSMJobArgs = @($Hostname,$Port,$HomeDirectory,$LogDirectory,$7DTDSMModulePath,$asJob)
            $7DTDSMJob = Start-Job -scriptblock {
            param ($Hostname, $Port, $HomeDirectory, $LogDirectory, $7DTDSMModulePath, $asJob)
            
                # Import Functions
                . $7DTDSMModulePath\modules\functions.ps1
                
                # 7DTDSM Server Custom Configuration
                $7DTDSMCustomConfigPath = $HomeDirectory + "\config.ps1"
    
                # Test Config Path
                $Test7DTDSMCustomConfigPath = Test-Path $7DTDSMCustomConfigPath
    
                if (!$Test7DTDSMCustomConfigPath)
                {            
                    # Import Config
                    . $7DTDSMModulePath\modules\config.ps1
                }
                else
                {
                    # Import Config
                    . $HomeDirectory\config.ps1
                }
                
                while ($true)
                {
                    Start-Sleep -s 60            
                    
                    # Get Job Time
                    $JobTime = Get-Date -format HHmm
                    
                    if ($LogSchedule -eq "Hourly")
                    {
                        # 7DTDSM Server Log Hashing (at *:30 hourly)
                        if ($JobTime -eq "*30")
                        {
                            New-7DTDSMLogHash -LogSchedule $LogSchedule -LogDirectory $LogDirectory
                        }
                    }
                    else
                    {
                        # 7DTDSM Server Log Hashing (at 02:30 daily)
                        if ($JobTime -eq "0230")
                        {
                            New-7DTDSMLogHash -LogSchedule $LogSchedule -LogDirectory $LogDirectory
                        }
                    }
                }
            } -ArgumentList $7DTDSMJobArgs
            
            # 7DTDSM Server Custom Background Jobs
            $7DTDSMCustomJobArgs = @($Hostname,$Port,$HomeDirectory,$LogDirectory,$7DTDSMModulePath,$CustomJob,$CustomJobSchedule,$asJob)
            $7DTDSMCustomJob = Start-Job -scriptblock {
            param ($Hostname, $Port, $HomeDirectory, $LogDirectory, $7DTDSMModulePath, $CustomJob, $CustomJobSchedule, $asJob)
            
                # Import Functions
                . $7DTDSMModulePath\modules\functions.ps1
                
                # 7DTDSM Server Custom Configuration
                $7DTDSMCustomConfigPath = $HomeDirectory + "\config.ps1"
    
                # Test Config Path
                $Test7DTDSMCustomConfigPath = Test-Path $7DTDSMCustomConfigPath
    
                if (!$Test7DTDSMCustomConfigPath)
                {            
                    # Import Config
                    . $7DTDSMModulePath\modules\config.ps1
                }
                else
                {
                    # Import Config
                    . $HomeDirectory\config.ps1
                }
                
                while ($true)
                {
                    Start-Sleep -s 60            
                    
                    # Get Job Time
                    $JobTime = Get-Date -format HHmm
                    
                    if ($CustomJobSchedule -eq "1")
                    {
                        # 7DTDSM Server Custom Jobs (at every 1 minute)
                        if ($CustomJob)
                        {
                            . $CustomJob
                        }
                    }                    
                    elseif ($CustomJobSchedule -eq "5")
                    {
                        # 7DTDSM Server Custom Jobs (at every 5 minutes)
                        if ($JobTime -like "*5" -or $JobTime -like "*0")
                        {
                            if ($CustomJob)
                            {
                                . $CustomJob
                            }
                        }
                    }
                    elseif ($CustomJobSchedule -eq "10")
                    {
                        # 7DTDSM Server Custom Jobs (at every 10 minutes)
                        if ($JobTime -like "*00" -or $JobTime -like "*10" -or $JobTime -like "*20" -or $JobTime -like "*30" -or $JobTime -like "*40" -or $JobTime -like "*50")
                        {
                            if ($CustomJob)
                            {
                                . $CustomJob
                            }
                        }
                    }
                    elseif ($CustomJobSchedule -eq "20")
                    {
                        # 7DTDSM Server Custom Jobs (at every 20 minutes)
                        if ($JobTime -like "*00" -or $JobTime -like "*20" -or $JobTime -like "*40")
                        {
                            if ($CustomJob)
                            {
                                . $CustomJob
                            }
                        }
                    }
                    elseif ($CustomJobSchedule -eq "30")
                    {
                        # 7DTDSM Server Custom Jobs (at every 30 minutes)
                        if ($JobTime -like "*00" -or $JobTime -like "*30")
                        {
                            if ($CustomJob)
                            {
                                . $CustomJob
                            }
                        }
                    }
                    elseif ($CustomJobSchedule -eq "60")
                    {
                        # 7DTDSM Server Custom Jobs (at every hour)
                        if ($JobTime -like "*00")
                        {
                            if ($CustomJob)
                            {
                                . $CustomJob
                            }
                        }
                    }                    
                    else
                    {
                        # 7DTDSM Server Custom Jobs (at every 5 minutes)
                        if ($JobTime -like "*5" -or $JobTime -like "*0")
                        {
                            if ($CustomJob)
                            {
                                . $CustomJob
                            }
                        }
                    }
                }
            } -ArgumentList $7DTDSMCustomJobArgs
            
            # 7DTDSM Server Custom Config
            if ($CustomConfig)
            {
                . $CustomConfig
            }
            
            # Create an HTTPListener
            try
            {
                $Listener = New-Object Net.HttpListener
            }
            catch
            {
                Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
            }
            
            # Add Prefix Urls
            try
            {
                if (!$Hostname) 
                {
                    $Hostname = "+"
                    
                    if (!$Port)
                    {
                        $Port = "8080"
                    }
                    
                    $Prefix = "http://" + $Hostname + ":" + $Port + "/"
                    $Listener.Prefixes.Add($Prefix)
                }
                else
                {
                    $Hostnames = @($Hostname.Split(","))
                    
                    if (!$Port)
                    {
                        $Port = "8080"
                    }
                            
                    foreach ($Hostname in $Hostnames)
                    {
                        $Prefix = "http://" + $Hostname + ":" + $Port + "/"
                        $Listener.Prefixes.Add($Prefix)
                    }
                }
                
                if ($SSL)
                {
                    if (!$SSLIP) 
                    {
                        $SSLIP = "127.0.0.1"
                        
                        if (!$SSLPort)
                        {
                            $SSLPort = "8443"
                        }
                        
                        $Prefix = "https://" + $SSLIP + ":" + $SSLPort + "/"
                        $Listener.Prefixes.Add($Prefix)
                    }
                    else
                    {
                        $SSLIPAddresses = @($SSLIP.Split(","))
                        
                        if (!$SSLPort)
                        {
                            $SSLPort = "8443"
                        }
                                
                        foreach ($SSLIPAddress in $SSLIPAddresses)
                        {
                            $Prefix = "https://" + $SSLIPAddress + ":" + $SSLPort + "/"
                            $Listener.Prefixes.Add($Prefix)
                        }
                    }
                }        
            }
            catch
            {
                Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
            }
            
            # Start Listener
            try
            {
                $Listener.Start()
            }
            catch
            {
                Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
            }
            
            # Configure SSL
            try
            {
                if ($SSL)
                {
                    if ($SSLName)
                    {
                        $7DTDSMCert = Get-ChildItem -Recurse Cert: | Where-Object { $_.FriendlyName -eq $SSLName }
                        
                        if (!$7DTDSMCert)
                        {
                            $7DTDSMCert = Get-ChildItem -Recurse Cert: | Where-Object { $_.FriendlyName -eq "7DaysToDieManager SSL Certificate" }
                        }
                    }
                    else
                    {
                        $7DTDSMCert = Get-ChildItem -Recurse Cert: | Where-Object { $_.FriendlyName -eq "7DaysToDieManager SSL Certificate" }
                    }
                    
                    if (!$7DTDSMCert)
                    {
                        if ($DebugMode)
                        {
                            Add-Content -Value "Sorry, I couldn't find your SSL certificate." -Path "$LogDirectory\debug.txt"
                            Add-Content -Value "Creating Self-Signed SSL certificate.." -Path "$LogDirectory\debug.txt"
                        }
                        Request-7DTDSMCertificate
                        $7DTDSMCert = Get-ChildItem -Recurse Cert: | Where-Object { $_.FriendlyName -eq "7DaysToDieManager SSL Certificate" }
                    }
                    
                    # Register SSL Certificate
                    $CertThumbprint = $7DTDSMCert[0].Thumbprint
                    Register-7DTDSMCertificate -SSLIP $SSLIP -SSLPort $SSLPort -Thumbprint $CertThumbprint -DebugMode $DebugMode
                }
            }
            catch
            {
                Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
            }
            
            # 7DTDSM Server Welcome Banner
            try
            {
                Get-7DTDSMWelcomeBanner -Hostname $Hostname -Port $Port -SSL $SSL -SSLIP $SSLIP -SSLPort $SSLPort -DebugMode $DebugMode
            }
            catch
            {
                Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
            }
            
            # 7DTDSM Server Async Process Script
            $ScriptBlock = `
            {
            Param($Listener, $Hostname, $Hostnames, $HomeDirectory, $LogDirectory, $7DTDSMModulePath, $CustomChildConfig, $DebugMode)
                
                # Import Functions
                . $7DTDSMModulePath\modules\functions.ps1
                
                # Enable Debug Mode
                if ($DebugMode)
                {
                    $DebugPreference = "Continue"
                }
                else
                {
                    $ErrorActionPreference = "silentlycontinue"
                }
                
                # PHP Encoding Module
                . $7DTDSMModulePath\modules\phpencoding.ps1
                
                # 7DTDSM Server Custom Child Config
                if ($CustomChildConfig)
                {
                    . $CustomChildConfig
                }
                
                # Create loop
                $ShouldProcess = $true
                
                # Get Server Requests
                while ($ShouldProcess)
                {        
                    # 7DTDSM Server Custom Configuration
                    $7DTDSMCustomConfigPath = $HomeDirectory + "\config.ps1"
        
                    # Test Config Path
                    $Test7DTDSMCustomConfigPath = Test-Path $7DTDSMCustomConfigPath
        
                    if (!$Test7DTDSMCustomConfigPath)
                    {            
                        # Import Config
                        . $7DTDSMModulePath\modules\config.ps1
                    }
                    else
                    {
                        # Import Config
                        . $HomeDirectory\config.ps1
                    }
                    
                    # Reset Authentication
                    $Listener.AuthenticationSchemes = "Anonymous";
                    
                    # Set Authentication
                    if ($BasicAuthentication -eq "On") { $Listener.AuthenticationSchemes = "Basic"; }
                    if ($NTLMAuthentication -eq "On") { $Listener.AuthenticationSchemes = "NTLM"; }
                    if ($WindowsAuthentication -eq "On") { $Listener.AuthenticationSchemes = "IntegratedWindowsAuthentication"; }

                    # Open Connection
                    $Context = $Listener.GetContext()
                    
                    # Authentication Module
                    . $7DTDSMModulePath\modules\authentication.ps1
                                
                    # Set Home Directory
                    [IO.Directory]::SetCurrentDirectory("$HomeDirectory")
                    $File = $Context.Request.Url.LocalPath
                    $Response = $Context.Response
                    $Response.Headers.Add("Accept-Encoding","gzip");
                    $Response.Headers.Add("Server","7 Days To Die Server Manager");
                    $Response.Headers.Add("X-Powered-By","Death");
                    
                    # Set Request Parameters
                    $Request = $Context.Request
                    $InputStream = $Request.InputStream
                    $ContentEncoding = $Request.ContentEncoding
                            
                    # IP Restriction Module
                    . $7DTDSMModulePath\modules\iprestriction.ps1
                    
                    # Get Query String
                    $7DTDSMQuery = Get-7DTDSMQueryString -Request $Request
                    
                    # Get Post Stream
                    $7DTDSMPost = Get-7DTDSMPostStream -InputStream $InputStream -ContentEncoding $ContentEncoding
                    
                    # Cookie Information
                    $7DTDSMCookies = $Request.Cookies["7DTDSMSessionID"];
                    if (!$7DTDSMCookies)
                    {
                        $7DTDSMCookie = New-Object Net.Cookie
                        $7DTDSMCookie.Name = "7DTDSMSessionID"
                        $7DTDSMCookie.Value = New-7DTDSMTimeStamp
                        $Response.AppendCookie($7DTDSMCookie)
                    }
                    
                    # Get Default Document
                    if ($File -notlike "*.*" -and $File -like "*/")
                    {
                        $FolderPath = [System.IO.Directory]::GetCurrentDirectory() + $File
                        $RequstURL = [string]$Request.Url
                        $SubfolderName = $File
                        $File = $File + $DefaultDocument
                    }
                    elseif ($File -notlike "*.*" -and $File -notlike "*/")
                    {
                        $FolderPath = [System.IO.Directory]::GetCurrentDirectory() + $File + "/"
                        $RequstURL = [string]$Request.Url + "/"
                        $SubfolderName = $File + "/"
                        $File = $File + "/" + $DefaultDocument 
                    }
                    else
                    {
                        $FolderPath = $Null;
                    }
                    
                    # 7DTDSM API Support
                    if ($File -like "*.psxml")
                    {
                        $File = $File.Replace(".psxml",".ps1")
                        
                        # Full File Path
                        $File = [System.IO.Directory]::GetCurrentDirectory() + $File
                        
                        # Get Mime Type
                        $MimeType = "text/psxml"
                    }
                    else
                    {
                        # Full File Path
                        $File = [System.IO.Directory]::GetCurrentDirectory() + $File
                        
                        # Get Mime Type
                        $FileExtension = (Get-ChildItem $File -EA SilentlyContinue).Extension
                        $MimeType = Get-MimeType $FileExtension
                    }
                    
                    # Content Filtering Module
                    . $7DTDSMModulePath\modules\contentfiltering.ps1
                    
                    # Stream Content
                    if ([System.IO.File]::Exists($File) -and $ContentSessionDrop -eq "0" -and $IPSessionDrop -eq "0")  
                    { 
                        if ($MimeType -eq "text/ps1")
                        {
                            try
                            {
                                $Response.ContentType = "text/html"
                                $Response.StatusCode = [System.Net.HttpStatusCode]::OK
                                $LogResponseStatus = $Response.StatusCode
                                $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                                $Response.WriteLine("$(. $File)")
                            }
                            catch
                            {
                                Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                            }
                        }
                        elseif ($MimeType -eq "text/psxml")
                        {
                            try
                            {
                                $Response.ContentType = "text/xml"
                                $Response.StatusCode = [System.Net.HttpStatusCode]::OK
                                $LogResponseStatus = $Response.StatusCode
                                $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                                $Response.WriteLine("$(. $File)")
                            }
                            catch
                            {
                                Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                            }
                        }
                        elseif ($MimeType -eq "text/php")
                        {
                            try
                            {
                                if ($PHPCgiPath)
                                {
                                    $TestPHPCgiPath = Test-Path -Path $PHPCgiPath
                                }
                                else
                                {
                                    $TestPHPCgiPath = $false
                                }
                                
                                if ($TestPHPCgiPath)
                                {
                                    if ($File -like "C:\Windows\*")
                                    {
                                        $Response.ContentType = "text/html"
                                        $Response.StatusCode = [System.Net.HttpStatusCode]::NotFound
                                        $LogResponseStatus = $Response.StatusCode
                                        $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                                        $Response.WriteLine("$(. $7DTDSMModulePath\modules\phpsecurityerror.ps1)")
                                    }
                                    else
                                    {
                                        $Response.ContentType = "text/html"
                                        $PHPContentOutput = Get-7DTDSMPHPContent -PHPCgiPath "$PHPCgiPath" -File "$File" -7DTDSMPHPGET $7DTDSMQuery.7DTDSMQueryString -7DTDSMPHPPOST $7DTDSMPost.7DTDSMPostStream
                                        $PHPContentOutput = Set-PHPEncoding -PHPOutput $PHPContentOutput
                                        $Response.StatusCode = [System.Net.HttpStatusCode]::OK
                                        $LogResponseStatus = $Response.StatusCode
                                        $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                                        $Response.WriteLine("$PHPContentOutput")
                                    }
                                }
                                else
                                {
                                    $Response.ContentType = "text/html"
                                    $Response.StatusCode = [System.Net.HttpStatusCode]::NotFound
                                    $LogResponseStatus = $Response.StatusCode
                                    $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                                    $Response.WriteLine("$(. $7DTDSMModulePath\modules\phpcgierror.ps1)")                        
                                }
                            }
                            catch
                            {
                                Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                            }
                        }                
                        else
                        {
                            try
                            {
                                $Response.ContentType = "$MimeType"
                                $FileContent = [System.IO.File]::ReadAllBytes($File)
                                $Response.ContentLength64 = $FileContent.Length
                                $Response.StatusCode = [System.Net.HttpStatusCode]::OK
                                $LogResponseStatus = $Response.StatusCode
                                $Response.OutputStream.Write($FileContent, 0, $FileContent.Length)
                            }
                            catch
                            {
                                Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                            }
                        }
                    }
                    else
                    {
                        # Content Filtering and IP Restriction Control
                        if ($ContentSessionDrop -eq "0" -and $IPSessionDrop -eq "0")
                        {
                            if ($FolderPath)
                            {
                                $TestFolderPath = Test-Path -Path $FolderPath
                            }
                            else
                            {
                                $TestFolderPath = $false
                            }
                        }
                        else
                        {
                            $TestFolderPath = $false
                        }
                        
                        if ($DirectoryBrowsing -eq "On" -and $TestFolderPath)
                        {
                            try
                            {
                                $Response.ContentType = "text/html"
                                $Response.StatusCode = [System.Net.HttpStatusCode]::OK
                                $LogResponseStatus = $Response.StatusCode
                                $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                                if ($Hostname -eq "+") { $HeaderName = "localhost" } else { $HeaderName = $Hostnames[0] }
                                $DirectoryContent = (Get-DirectoryContent -Path "$FolderPath" -HeaderName $HeaderName -RequestURL $RequestURL -SubfolderName $SubfolderName)
                                $Response.WriteLine("$DirectoryContent")
                            }
                            catch
                            {
                                Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                            }
                        }
                        else
                        {
                            try
                            {
                                $Response.ContentType = "text/html"
                                $Response.StatusCode = [System.Net.HttpStatusCode]::NotFound
                                $LogResponseStatus = $Response.StatusCode
                                $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                                $Response.WriteLine("$(. $7DTDSMModulePath\modules\notfound.ps1)")
                            }
                            catch
                            {
                                Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                            }
                        }
                    }
                    
                    # Logging Module
                    . $7DTDSMModulePath\modules\log.ps1
                    
                    # Close Connection
                    try
                    {
                        $Response.Close()
                    }
                    catch
                    {
                        Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                    }
                }    
            }
            
            if ($DebugMode)
            {    
                # Invoke 7DTDSM Server Multithread Process - Thread 1
                Invoke-AsyncHTTPRequest -ScriptBlock $ScriptBlock -Listener $Listener -Hostname $Hostname -Hostnames $Hostnames -HomeDirectory $HomeDirectory -LogDirectory $LogDirectory -7DTDSMModulePath $7DTDSMModulePath -CustomChildConfig $CustomChildConfig -DebugMode | Out-Null
                
                # Invoke 7DTDSM Server Multithread Process - Thread 2
                Invoke-AsyncHTTPRequest -ScriptBlock $ScriptBlock -Listener $Listener -Hostname $Hostname -Hostnames $Hostnames -HomeDirectory $HomeDirectory -LogDirectory $LogDirectory -7DTDSMModulePath $7DTDSMModulePath -CustomChildConfig $CustomChildConfig -DebugMode | Out-Null
                
                # Invoke 7DTDSM Server Multithread Process - Thread 3
                Invoke-AsyncHTTPRequest -ScriptBlock $ScriptBlock -Listener $Listener -Hostname $Hostname -Hostnames $Hostnames -HomeDirectory $HomeDirectory -LogDirectory $LogDirectory -7DTDSMModulePath $7DTDSMModulePath -CustomChildConfig $CustomChildConfig -DebugMode | Out-Null
            }
            else
            {
                # Invoke 7DTDSM Server Multithread Process - Thread 1
                Invoke-AsyncHTTPRequest -ScriptBlock $ScriptBlock -Listener $Listener -Hostname $Hostname -Hostnames $Hostnames -HomeDirectory $HomeDirectory -LogDirectory $LogDirectory -7DTDSMModulePath $7DTDSMModulePath -CustomChildConfig $CustomChildConfig | Out-Null
                
                # Invoke 7DTDSM Server Multithread Process - Thread 2
                Invoke-AsyncHTTPRequest -ScriptBlock $ScriptBlock -Listener $Listener -Hostname $Hostname -Hostnames $Hostnames -HomeDirectory $HomeDirectory -LogDirectory $LogDirectory -7DTDSMModulePath $7DTDSMModulePath -CustomChildConfig $CustomChildConfig | Out-Null
                
                # Invoke 7DTDSM Server Multithread Process - Thread 3
                Invoke-AsyncHTTPRequest -ScriptBlock $ScriptBlock -Listener $Listener -Hostname $Hostname -Hostnames $Hostnames -HomeDirectory $HomeDirectory -LogDirectory $LogDirectory -7DTDSMModulePath $7DTDSMModulePath -CustomChildConfig $CustomChildConfig | Out-Null
            }
            
            # Create loop
            $ShouldProcess = $true
            
            # Get Server Requests
            while ($ShouldProcess)
            {
                # 7DTDSM Server Custom Configuration
                $7DTDSMCustomConfigPath = $HomeDirectory + "\config.ps1"
    
                # Test Config Path
                $Test7DTDSMCustomConfigPath = Test-Path $7DTDSMCustomConfigPath
    
                if (!$Test7DTDSMCustomConfigPath)
                {            
                    # Import Config
                    . $7DTDSMModulePath\modules\config.ps1
                }
                else
                {
                    # Import Config
                    . $HomeDirectory\config.ps1
                }
                
                # Reset Authentication
                $Listener.AuthenticationSchemes = "Anonymous";
                
                # Set Authentication
                if ($BasicAuthentication -eq "On") { $Listener.AuthenticationSchemes = "Basic"; }
                if ($NTLMAuthentication -eq "On") { $Listener.AuthenticationSchemes = "NTLM"; }
                if ($WindowsAuthentication -eq "On") { $Listener.AuthenticationSchemes = "IntegratedWindowsAuthentication"; }

                # Open Connection
                $Context = $Listener.GetContext()
                
                # Authentication Module
                . $7DTDSMModulePath\modules\authentication.ps1
                            
                # Set Home Directory
                [IO.Directory]::SetCurrentDirectory("$HomeDirectory")
                $File = $Context.Request.Url.LocalPath
                $Response = $Context.Response
                $Response.Headers.Add("Accept-Encoding","gzip");
                $Response.Headers.Add("Server","7DTDSM Server");
                $Response.Headers.Add("X-Powered-By","Microsoft PowerShell");
                
                # Set Request Parameters
                $Request = $Context.Request
                $InputStream = $Request.InputStream
                $ContentEncoding = $Request.ContentEncoding
                        
                # IP Restriction Module
                . $7DTDSMModulePath\modules\iprestriction.ps1
                
                # Get Query String
                $7DTDSMQuery = Get-7DTDSMQueryString -Request $Request
                
                # Get Post Stream
                $7DTDSMPost = Get-7DTDSMPostStream -InputStream $InputStream -ContentEncoding $ContentEncoding
                
                # Cookie Information
                $7DTDSMCookies = $Request.Cookies["7DTDSMSessionID"];
                if (!$7DTDSMCookies)
                {
                    $7DTDSMCookie = New-Object Net.Cookie
                    $7DTDSMCookie.Name = "7DTDSMSessionID"
                    $7DTDSMCookie.Value = New-7DTDSMTimeStamp
                    $Response.AppendCookie($7DTDSMCookie)
                }
                
                # Get Default Document
                if ($File -notlike "*.*" -and $File -like "*/")
                {
                    $FolderPath = [System.IO.Directory]::GetCurrentDirectory() + $File
                    $RequstURL = [string]$Request.Url
                    $SubfolderName = $File
                    $File = $File + $DefaultDocument
                }
                elseif ($File -notlike "*.*" -and $File -notlike "*/")
                {
                    $FolderPath = [System.IO.Directory]::GetCurrentDirectory() + $File + "/"
                    $RequstURL = [string]$Request.Url + "/"
                    $SubfolderName = $File + "/"
                    $File = $File + "/" + $DefaultDocument 
                }
                else
                {
                    $FolderPath = $Null;
                }
                
                # 7DTDSM API Support
                if ($File -like "*.psxml")
                {
                    $File = $File.Replace(".psxml",".ps1")
                    
                    # Full File Path
                    $File = [System.IO.Directory]::GetCurrentDirectory() + $File
                    
                    # Get Mime Type
                    $MimeType = "text/psxml"
                }
                else
                {
                    # Full File Path
                    $File = [System.IO.Directory]::GetCurrentDirectory() + $File
                    
                    # Get Mime Type
                    $FileExtension = (Get-ChildItem $File -EA SilentlyContinue).Extension
                    $MimeType = Get-MimeType $FileExtension
                }
                
                # Content Filtering Module
                . $7DTDSMModulePath\modules\contentfiltering.ps1
                
                # Stream Content
                if ([System.IO.File]::Exists($File) -and $ContentSessionDrop -eq "0" -and $IPSessionDrop -eq "0")  
                { 
                    if ($MimeType -eq "text/ps1")
                    {
                        try
                        {
                            $Response.ContentType = "text/html"
                            $Response.StatusCode = [System.Net.HttpStatusCode]::OK
                            $LogResponseStatus = $Response.StatusCode
                            $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                            $Response.WriteLine("$(. $File)")
                        }
                        catch
                        {
                            Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                        }
                    }
                    elseif ($MimeType -eq "text/psxml")
                    {
                        try
                        {
                            $Response.ContentType = "text/xml"
                            $Response.StatusCode = [System.Net.HttpStatusCode]::OK
                            $LogResponseStatus = $Response.StatusCode
                            $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                            $Response.WriteLine("$(. $File)")
                        }
                        catch
                        {
                            Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                        }
                    }
                    elseif ($MimeType -eq "text/php")
                    {
                        try
                        {
                            if ($PHPCgiPath)
                            {
                                $TestPHPCgiPath = Test-Path -Path $PHPCgiPath
                            }
                            else
                            {
                                $TestPHPCgiPath = $false
                            }
                            
                            if ($TestPHPCgiPath)
                            {
                                if ($File -like "C:\Windows\*")
                                {
                                    $Response.ContentType = "text/html"
                                    $Response.StatusCode = [System.Net.HttpStatusCode]::NotFound
                                    $LogResponseStatus = $Response.StatusCode
                                    $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                                    $Response.WriteLine("$(. $7DTDSMModulePath\modules\phpsecurityerror.ps1)")
                                }
                                else
                                {
                                    $Response.ContentType = "text/html"
                                    $PHPContentOutput = Get-7DTDSMPHPContent -PHPCgiPath "$PHPCgiPath" -File "$File" -7DTDSMPHPGET $7DTDSMQuery.7DTDSMQueryString -7DTDSMPHPPOST $7DTDSMPost.7DTDSMPostStream
                                    $PHPContentOutput = Set-PHPEncoding -PHPOutput $PHPContentOutput
                                    $Response.StatusCode = [System.Net.HttpStatusCode]::OK
                                    $LogResponseStatus = $Response.StatusCode
                                    $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                                    $Response.WriteLine("$PHPContentOutput")
                                }
                            }
                            else
                            {
                                $Response.ContentType = "text/html"
                                $Response.StatusCode = [System.Net.HttpStatusCode]::NotFound
                                $LogResponseStatus = $Response.StatusCode
                                $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                                $Response.WriteLine("$(. $7DTDSMModulePath\modules\phpcgierror.ps1)")                        
                            }
                        }
                        catch
                        {
                            Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                        }
                    }                
                    else
                    {
                        try
                        {
                            $Response.ContentType = "$MimeType"
                            $FileContent = [System.IO.File]::ReadAllBytes($File)
                            $Response.ContentLength64 = $FileContent.Length
                            $Response.StatusCode = [System.Net.HttpStatusCode]::OK
                            $LogResponseStatus = $Response.StatusCode
                            $Response.OutputStream.Write($FileContent, 0, $FileContent.Length)
                        }
                        catch
                        {
                            Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                        }
                    }
                }
                else
                {
                    # Content Filtering and IP Restriction Control
                    if ($ContentSessionDrop -eq "0" -and $IPSessionDrop -eq "0")
                    {
                        if ($FolderPath)
                        {
                            $TestFolderPath = Test-Path -Path $FolderPath
                        }
                        else
                        {
                            $TestFolderPath = $false
                        }
                    }
                    else
                    {
                        $TestFolderPath = $false
                    }
                    
                    if ($DirectoryBrowsing -eq "On" -and $TestFolderPath)
                    {
                        try
                        {
                            $Response.ContentType = "text/html"
                            $Response.StatusCode = [System.Net.HttpStatusCode]::OK
                            $LogResponseStatus = $Response.StatusCode
                            $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                            if ($Hostname -eq "+") { $HeaderName = "localhost" } else { $HeaderName = $Hostnames[0] }
                            $DirectoryContent = (Get-DirectoryContent -Path "$FolderPath" -HeaderName $HeaderName -RequestURL $RequestURL -SubfolderName $SubfolderName)
                            $Response.WriteLine("$DirectoryContent")
                        }
                        catch
                        {
                            Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                        }
                    }
                    else
                    {
                        try
                        {
                            $Response.ContentType = "text/html"
                            $Response.StatusCode = [System.Net.HttpStatusCode]::NotFound
                            $LogResponseStatus = $Response.StatusCode
                            $Response = New-Object IO.StreamWriter($Response.OutputStream,[Text.Encoding]::UTF8)
                            $Response.WriteLine("$(. $7DTDSMModulePath\modules\notfound.ps1)")
                        }
                        catch
                        {
                            Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                        }
                    }
                }
                
                # Logging Module
                . $7DTDSMModulePath\modules\log.ps1
                
                # Close Connection
                try
                {
                    $Response.Close()
                }
                catch
                {
                    Add-Content -Value $_ -Path "$LogDirectory\debug.txt"
                }
            }
            
            # Stop Listener
            $Listener.Stop()
        }
    }
}