cIBMHTTPServerUtils.psm1

##############################################################################################################
######## IBM HTTP Server CmdLets #########
##############################################################################################################

enum StartupType {
    Automatic
    Manual
    Disabled
}

# Global Variables / Resource Configuration
$IHS_SVC_PREFIX = "IBMHTTPServerV"

##############################################################################################################
# Get-IBMHTTPServerInstallLocation
# Returns the location where IBM HTTP Server is installed
##############################################################################################################
Function Get-IBMHTTPServerInstallLocation() {
    [CmdletBinding(SupportsShouldProcess=$False)]
    param (
        [parameter(Mandatory=$false,position=1)]
        [System.Version] $Version = "8.5.0.0"
    )

    Write-Verbose "Get-IBMHTTPServerInstallLocation::ENTRY(Version=$Version)"
    
    $ihsPath = Get-IBMWebSphereProductRegistryPath "HTTP Server" $Version
    if ($ihsPath -and $ihsPath.StartsWith("HKU:")) {
        New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS | Out-Null
    }
    
    if (($ihsPath) -and (Test-Path($ihsPath))) {
        $ihsHome = (Get-ItemProperty($ihsPath)).installPath
        if ($ihsHome -and (Test-Path $ihsHome)) {
            Write-Verbose "Get-IBMHTTPServerInstallLocation returning $ihsHome"
            Return $ihsHome
        }
    }
    Return $null
}

##############################################################################################################
# Install-IBMHTTPServer
# Installs IBM HTTP Server
##############################################################################################################
Function Install-IBMHTTPServer() {
    [CmdletBinding(SupportsShouldProcess=$False)]
    param (
        [parameter(Mandatory = $true)]
        [String] $InstallMediaConfig,
        
        [parameter(Mandatory = $true)]
        [String] $ResponseFileTemplate,
        
        [parameter(Mandatory = $true)]
        [String] $InstallationDirectory,
        
        [Int] $HTTPPort = 80,
        
        [StartupType] $StartupType = [StartupType]::Automatic,
        
        [PSCredential] $WindowsServiceAccount,
        
        [parameter(Mandatory = $true)]
        [String] $IMSharedLocation,

        [parameter(Mandatory = $true)]
        [String] $SourcePath,

        [PSCredential] $SourcePathCredential
    )
    
    $installed = $false
    [Hashtable] $Variables = @{}
    $Variables.Add("sharedLocation", $IMSharedLocation)
    $Variables.Add("ihsInstallLocation", $InstallationDirectory)
    $Variables.Add("httpPort", $HTTPPort)
    if ($StartupType -eq [StartupType]::Automatic) {
        $Variables.Add("serviceStartType", "auto")
    } elseif ($StartupType -eq [StartupType]::Manual) {
        $Variables.Add("serviceStartType", "demand")
    }
    if ($WindowsServiceAccount) {
        $Variables.Add("serviceAccUsername", $WindowsServiceAccount.UserName)
        $Variables.Add("serviceAccPassword", $WindowsServiceAccount)
    } else {
        $Variables.Add("useServiceAcc", "true")
    }
    
    $portOpen = Test-NetConnection -ComputerName localhost -Port $HTTPPort -InformationLevel Quiet
    if (!$portOpen) {
        $installed = Install-IBMProduct -InstallMediaConfig $InstallMediaConfig `
                        -ResponseFileTemplate $ResponseFileTemplate -Variables $Variables `
                        -SourcePath $SourcePath -SourcePathCredential $SourcePathCredential -ErrorAction Stop
    } else {
        Write-Error "The port $HTTPPort is already in used, please use a different port"
    }
    
    if ($installed) {
        Start-IBMHTTPServer
    }

    Return $installed
}

##############################################################################################################
# Start-IBMHTTPServer
# Starts the IBM HTTP Server
##############################################################################################################
Function Start-IBMHTTPServer {
    [CmdletBinding(SupportsShouldProcess=$False)]
    Param ()

    $ihsSvcName = $IHS_SVC_PREFIX + "*"
    $ihsSvc = Get-Service -Name $ihsSvcName
    
    if ($ihsSvc) {
        if ($ihsSvc.Status -ne "Running") {
            Write-Verbose "Starting IBM HTTP Server via Windows Service"
            Start-Service $ihsSvc
        } else {
            Write-Verbose "IBM HTTP Server already started"
        }
    }
}

##############################################################################################################
# Stop-IBMHTTPServer
# Stops the IBM HTTP Server
##############################################################################################################
Function Stop-IBMHTTPServer {
    [CmdletBinding(SupportsShouldProcess=$False)]
    Param ()

    $ihsSvcName = $IHS_SVC_PREFIX + "*"
    $ihsSvc = Get-Service -Name $ihsSvcName
    
    if ($ihsSvc) {
        if ($ihsSvc.Status -ne "Stopped") {
            Write-Verbose "Stopping IBM HTTP Server via Windows Service"
            Stop-Service $ihsSvc
        } else {
            Write-Verbose "IBM HTTP Server already stopped"
        }
    }
}

##############################################################################################################
# Invoke-GskCmd
# Wrapper function for gskcmd.bat
##############################################################################################################
Function Invoke-GskCmd() {
    [CmdletBinding(SupportsShouldProcess=$False)]
    Param (
        [Parameter(Mandatory=$true,position=0)]
        [String[]] $Commands,
        
        [Parameter(Mandatory=$false,position=1)]
        [PSCredential] $KeyDBPassword,
        
        [switch] $Target
    )

    $ihsInstallDir = Get-IBMHTTPServerInstallLocation
    [string] $gskcmdBat = Join-Path -Path $ihsInstallDir -ChildPath "bin\gskcmd.bat"
    [PSCustomObject] $gskCmdProcess = @{
        StdOut = $null
        StdErr = $null
        ExitCode = $null
    }
    if (Test-Path($gskcmdBat)) {
        [string[]] $gskArgs = $Commands
        # Add credentials
        if ($KeyDBPassword) {
            $keyPwd = $KeyDBPassword.GetNetworkCredential().Password
            if ($Target) {
                $gskArgs += @("-target_pw", $keyPwd)
            } else {
                $gskArgs += @("-pw", $keyPwd)
            }
        }
        $gskCmdProcess = Invoke-ProcessHelper -ProcessFileName $gskcmdBat -ProcessArguments $gskArgs `
                            -WorkingDirectory (Split-Path($gskcmdBat))
        if (!$gskCmdProcess -or (($gskCmdProcess.StdErr)) -and ($gskCmdProcess.ExitCode -ne 0)) {
            $errorMsg = $null
            if ($gskCmdProcess -and $gskCmdProcess.StdErr) {
                $errorMsg = $gskCmdProcess.StdErr
            } else {
                $errorMsg = $gskCmdProcess.StdOut
            }
            $exitCode = (&{if($gskCmdProcess) {$gskCmdProcess.ExitCode} else {$null}})
            Write-Error "An error occurred while executing the gskcmd.bat process. ExitCode: $exitCode Mesage: $errorMsg"
        }
    } else {
        Write-Error "Unable to locate gskcmd.bat using: $gskcmdBat"
    }
    Return $gskCmdProcess
}

##############################################################################################################
# New-IBMSSLKeyDatabase
# Creates a new key database
##############################################################################################################
Function New-IBMSSLKeyDatabase() {
    [CmdletBinding(SupportsShouldProcess=$False)]
    Param (
        [Parameter(Mandatory=$true,position=0)]
        [String] $KeyDBPath,
        
        [Parameter(Mandatory=$true,position=1)]
        [PSCredential] $KeyDBPassword,

        [Parameter(Mandatory=$false,position=2)]
        [String] $DBType = 'cms',

        [switch] $Stash
    )

    $keydbCreated = $false

    if (!(Test-Path $KeyDBPath -PathType Leaf)) {
        if (!(Test-Path (Split-Path $KeyDBPath))) {
            # Parent folder does not exist, create it
            New-Item -Path (Split-Path $KeyDBPath) -Type Directory -Force | Out-Null
            if (!(Test-Path (Split-Path $KeyDBPath))) {
                Write-Error "Unable to create parent folder for key database"
            }
        }
        [string[]] $gskArgs = @("-keydb","-create","-db",$KeyDBPath,"-type",$DBType)

        if ($Stash) {
            $gskArgs += "-stash"
        }
        
        $gskCmdProcess = Invoke-GskCmd -Commands $gskArgs -KeyDBPassword $KeyDBPassword
        if ($gskCmdProcess) {
            if (([string]$gskCmdProcess.StdOut).Trim().Length -eq 0) {
                $keydbCreated = $true
            } else {
                $errorMsg = $gskCmdProcess.StdOut
                Write-Error "An error ocurred while creating the Key Database: $errorMsg"
            }
        }
    } else {
        Write-Error "Key Database already exits or path is invalid: $KeyDBPath"
    }
    Return $keydbCreated
}

##############################################################################################################
# Get-IBMKeyDBCertificates
# Returns a list of SSL certificate labels stored in a Key Database
##############################################################################################################
Function Get-IBMKeyDBCertificates() {
    [CmdletBinding(SupportsShouldProcess=$False)]
    Param (
        [Parameter(Mandatory=$true,position=0)]
        [String] $KeyDBPath,
        
        [Parameter(Mandatory=$true,position=1)]
        [PSCredential] $KeyDBPassword
    )
    [string[]] $certificateLabels = $null

    if (Test-Path $KeyDBPath -PathType Leaf) {
        [string[]] $gskArgs = @("-cert","-list","-db",$KeyDBPath)
        $gskCmdProcess = Invoke-GskCmd -Commands $gskArgs -KeyDBPassword $KeyDBPassword
        if ($gskCmdProcess -and $gskCmdProcess.StdOut) {
            if ($gskCmdProcess.StdOut.Contains("Certificates in database")) {
                $certificateLabels = @()
                ($gskCmdProcess.StdOut -split [environment]::NewLine) | ? {
                    if (!([string]$_).Contains("Certificates in database")) {
                        $certificateLabels += ([string]$_).Trim()
                    }
                }
            } else {
                $errorMsg = $gskCmdProcess.StdOut
                Write-Error "An error ocurred while listing the certificates: $errorMsg"
            }
        }
    } else {
        Write-Error "Invalid Key Database"
    }
    Return $certificateLabels
}

##############################################################################################################
# Get-IBMKeyDBDefaultCertificate
# Returns the default SSL certificate label for the specified Key Database
##############################################################################################################
Function Get-IBMKeyDBDefaultCertificate() {
    [CmdletBinding(SupportsShouldProcess=$False)]
    Param (
        [Parameter(Mandatory=$true,position=0)]
        [String] $KeyDBPath,
        
        [Parameter(Mandatory=$true,position=1)]
        [PSCredential] $KeyDBPassword
    )
    [string] $certificateLabel = $null

    if (Test-Path $KeyDBPath -PathType Leaf) {
        [string[]] $gskArgs = @("-cert","-getdefault","-db",$KeyDBPath)
        $gskCmdProcess = Invoke-GskCmd -Commands $gskArgs -KeyDBPassword $KeyDBPassword
        if ($gskCmdProcess -and $gskCmdProcess.StdOut) {
            [string] $labelStr = "Label: "
            if ($gskCmdProcess.StdOut.Contains($labelStr)) {
                ($gskCmdProcess.StdOut -split [environment]::NewLine) | ? {
                    if (([string]$_).Contains($labelStr)) {
                        $certificateLabel = ([string]$_).Trim().Substring($labelStr.Length)
                    }
                }
            } else {
                $errorMsg = $gskCmdProcess.StdOut
                Write-Error "An error ocurred while listing the certificates: $errorMsg"
            }
        }
    } else {
        Write-Error "Invalid Key Database"
    }
    Return $certificateLabel
}

##############################################################################################################
# Add-SSLCertificateToIBMKeyDB
# Adds an SSL Certificate to the IBM Key Database specified
##############################################################################################################
Function Add-SSLCertificateToIBMKeyDB() {
    [CmdletBinding(SupportsShouldProcess=$False)]
    Param (
        [Parameter(Mandatory=$true)]
        [String] $KeyDBPath,
        
        [Parameter(Mandatory=$true)]
        [PSCredential] $KeyDBPassword,

        [Parameter(Mandatory=$false)]
        [String] $CertificateType = "pfx",

        [Parameter(Mandatory=$true)]
        [String] $CertificatePath,

        [Parameter(Mandatory=$true)]
        [PSCredential] $CertificatePassword,

        [PSCredential] $CertificatePathCredential,

        [String] $DefaultCertificateLabel
    )

    $certAdded = $false

    if (Test-Path $KeyDBPath -PathType Leaf) {
        $networkShare = $false
        try {
            if (($CertificatePath.StartsWith("\\")) -and (!(Test-Path($CertificatePath)))) {
                $networkShare = $true
            }
        } catch [System.UnauthorizedAccessException] {
            $networkShare = $true
        }

        $certTempPath = $CertificatePath

        if ($networkShare) {
            Write-Verbose "Network Share detected, need to map"
            Set-NetUse -SharePath (Split-Path($CertificatePath)) -SharePathCredential $CertificatePathCredential -Ensure "Present" | Out-Null
            try {
                Copy-Item $CertificatePath (Join-Path (Get-IBMTempDir) (Split-Path $CertificatePath -Leaf)) -Force
            } catch {
                $ErrorMessage = $_.Exception.Message
                Write-Error "An error occurred while copying the SSL certificate: $CertificatePath \n Error Message: $ErrorMessage"
            } finally {
                try {
                    Set-NetUse -SharePath (Split-Path($CertificatePath)) -SharePathCredential $CertificatePathCredential -Ensure "Absent" | Out-Null
                } catch {
                    Write-Warning "Unable to disconnect share: $CertificatePath"
                }
            }
        }

        [string[]] $gskArgs = @("-cert","-import","-file",$certTempPath)
        $certPwd = $CertificatePassword.GetNetworkCredential().Password
        $gskArgs += @("-pw", $certPwd)
        $gskArgs += @("-target", $KeyDBPath, "-$CertificateType")
        
        $gskCmdProcess = Invoke-GskCmd -Commands $gskArgs -KeyDBPassword $KeyDBPassword -Target
        if ($gskCmdProcess) {
            if (([string]$gskCmdProcess.StdOut).Trim().Length -eq 0) {
                $certAdded = $true
            } else {
                $errorMsg = $gskCmdProcess.StdOut
                Write-Error "An error ocurred while adding the certificate to the Key Database: $errorMsg"
            }
        }

        if ($certAdded -and (!([string]::IsNullOrEmpty($DefaultCertificateLabel)))) {
            $gskSetDefaultArgs = @("-cert", "-setdefault", "-db", $KeyDBPath, "-label",$DefaultCertificateLabel)
            $gskCmdProcess = Invoke-GskCmd -Commands $gskSetDefaultArgs -KeyDBPassword $KeyDBPassword
            $certAdded = $false
            if ($gskCmdProcess) {
                if (([string]$gskCmdProcess.StdOut).Trim().Length -eq 0) {
                    $certAdded = $true
                } else {
                    $errorMsg = $gskCmdProcess.StdOut
                    Write-Error "An error ocurred setting the certificate label $DefaultCertificateLabel as default: $errorMsg"
                }
            }
        }
    } else {
        Write-Error "Invalid Key Database: $KeyDBPath"
    }
    Return $certAdded
}