Terminal.ps1

$ScriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent

. ($ScriptDir + "./Global.ps1")

$TerminalProductName =  "logeto-terminal"
$TerminalPackageName = "B158BDD8.LogetoTerminal"
$TerminalProcessName = "Logeto.Client.Windows.Terminal"

$MinWinVersionMajor = 10
$MinWinVersionMinor = 0
$MinWinVersionBuild = 14393

<#
    Return Json file for Terminal appx, if there is no newer version null is returned.
#>

function Get-LogetoTerminalUpdateInfo 
{
    Write-LogetoDebug "Getting Terminal Update Info." 

    $scope = Get-LogetoScope
    $appxVersion = Get-LogetoAppxVersion $TerminalPackageName        
    return Get-LogetoUpdateInfo -Product $TerminalProductName -Scope $scope -Version $appxVersion
}

<#
    Install logeto-terminal product
#>

function Install-LogetoTerminal {

    Param(
        [parameter(Mandatory=$true)]
        $UpdateInfo
    )

    Write-LogetoDebug "Installing terminal" 

    Get-LogetoProduct $UpdateInfo $ProductName
    
    # Support for uninstall
    $keyname = "LogetoTerminal"
    $regname = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
    $fullpath = $regname + "\" + $keyname

    if (-Not (Test-Path $fullpath))
    {
        New-Item -Path $regname -Name $keyname   
    }

    New-ItemProperty -Path $fullpath -Name "DisplayName" -Value "Logeto Terminal" -Force

    $value = $AppFolderPath + $ProductName + "Uninstall\LogetoInstaller.exe -uninstallAsCurrentUser"
    New-ItemProperty -Path $fullpath -Name "UninstallString" -Value $value -Force

    New-ItemProperty -Path $fullpath -Name "Publisher" -Value "Systemart s.r.o." -Force

    $value = $AppFolderPath + $ProductName + "Uninstall\ProductLogo.ico"
    New-ItemProperty -Path $fullpath -Name "DisplayIcon" -Value $value -Force

    Update-Package

    #Register task
    $A = New-ScheduledTaskAction �Execute $AppFolderPath\$ServiceProductName\TerminalAppxUpdate.vbs `"$AppFolderPath\$ServiceProductName\TerminalAppxUpdate.ps1`"
    $T = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 60)
    $U = $(whoami)
    $N = [Environment]::UserName
    $S = New-ScheduledTaskSettingsSet
    $name = "Logeto Terminal Appx Update ($N)"
    $taskExists = (Get-ScheduledTask | Where-Object {$_.TaskName -eq $name })
    if ($taskExists)
    {
        Write-LogetoDebug "Logeto Terminal Appx Update task exists"
        Write-LogetoProgress "Removing old appx task" "ScriptRegisterAppxUpdateTaskRemovingTask"
        UnRegister-ScheduledTask -TaskName $name -Confirm:$false
    } else
    {
        Write-LogetoDebug "Logeto Terminal Appx Update task do not exists"
    }
    Write-LogetoProgress "Registering new appx task" "ScriptRegisterAppxUpdateTaskRegisteringTask"
    Register-ScheduledTask -TaskName $name -Action $A -Trigger $T -Settings $S -User $U -Force
}

Function Get-DependencyPackages
{
    $dependencyPackagesDir = (Join-Path $DownloadFolderPath\$TerminalProductName "\Dependencies")
    $dependencyPackages = @()
    if (Test-Path $dependencyPackagesDir)
    {
        # Get architecture-neutral dependencies
        $dependencyPackages += Get-ChildItem (Join-Path $dependencyPackagesDir "*.appx") | Where-Object { $_.Mode -NotMatch "d" }

        # Get architecture-specific dependencies
        if (($Env:Processor_Architecture -eq "x86" -or $Env:Processor_Architecture -eq "amd64") -and (Test-Path (Join-Path $dependencyPackagesDir "x86")))
        {
            $dependencyPackages += Get-ChildItem (Join-Path $dependencyPackagesDir "x86\*.appx") | Where-Object { $_.Mode -NotMatch "d" }
        }
        if (($Env:Processor_Architecture -eq "amd64") -and (Test-Path (Join-Path $dependencyPackagesDir "x64")))
        {
            $dependencyPackages += Get-ChildItem (Join-Path $dependencyPackagesDir "x64\*.appx") | Where-Object { $_.Mode -NotMatch "d" }
        }
        if (($Env:Processor_Architecture -eq "arm") -and (Test-Path (Join-Path $dependencyPackagesDir "arm")))
        {
            $dependencyPackages += Get-ChildItem (Join-Path $dependencyPackagesDir "arm\*.appx") | Where-Object { $_.Mode -NotMatch "d" }
        }
    }
    if ($dependencyPackages.FullName.Count -gt 0)
    {
        Write-Host "Found $($dependencyPackages.FullName.Count) dependecies:" -ForegroundColor Cyan
        Write-Host ($dependencyPackages.FullName | Out-String)
    }

    $global:dependecies = $dependencyPackages
}

Function Test-PlatformAndPackageFile($processorArch, $appxArch)
{
    $path = Get-ChildItem (Join-Path $DownloadFolderPath\$TerminalProductName "\*_$appxArch.appx")
    if (($Env:Processor_Architecture -eq $processorArch) -and ($path))
    {
        return $path
    }
}

Function Get-PlatformPackage
{
    if ($appx = Test-PlatformAndPackageFile 'amd64' 'x64')
    {
        return $appx
    }
    if (($appx = Test-PlatformAndPackageFile 'x86' 'x86') -or ($appx = Test-PlatformAndPackageFile 'amd64' 'x86'))
    {
        return $appx
    }
    if ($appx = Test-PlatformAndPackageFile 'arm' 'arm')
    {
        return $appx
    }
}

Function Update-Package
{
    Write-Host "Updating package `"$TerminalPackageName`"" -ForegroundColor Yellow

    $addPackageSucceeded = $false

    Write-Host "Phase 1"

    Test-WindowsVersion

    Write-Host "Phase 2"

    Get-DependencyPackages

    Write-Host "Phase 3"

    $packagePath = Get-PlatformPackage
    if (!($packagePath))
    {
        throw [System.IO.FileNotFoundException] "Appx file not found"
    }

    Write-Host "Phase 4"

    if ($TerminalProcessName)
    {
        $appProcess = Get-Process $TerminalProcessName -ErrorAction SilentlyContinue

        Write-Host "Phase 5 $appProcess"
    }

    $aaProcess = Get-Process "AssignedAccessLockApp" -ErrorAction SilentlyContinue

    $package = Get-AppxPackage -Name "$TerminalPackageName"
    if ($package)
    {
        Write-Host "Stopping service process"

        Stop-Process -Name $TerminalProcessName -ErrorAction SilentlyContinue

        Write-Host "Waiting for 5 seconds"

        Start-Sleep -s 5
    }

    Write-Host "Found appx: $packagePath"

    Write-Host "Installing new appx..." -ForegroundColor Cyan

    Write-Host "Dependencies count $global:dependecies.FullName.Count"

    if ($global:dependecies.FullName.Count -gt 0)
    {
        Add-AppxPackage -Path $packagePath.FullName -DependencyPath $global:dependecies.FullName -ForceApplicationShutdown -Verbose -EV addPackageError
    }
    else
    {
        Add-AppxPackage -Path $packagePath.FullName -ForceApplicationShutdown -Verbose -EV addPackageError
    }
    $addPackageSucceeded = $?

    if (!$addPackageSucceeded)
    {
        $reportMessage = "Failed to update package `"$TerminalPackageName`""
        Write-Host $reportMessage -ForegroundColor Red -BackgroundColor Black

        if ($addPackageError -and ($addPackageError.Count -gt 0))
        {
            $found = $addPackageError[0] -match 'Get-AppxLog -ActivityID (?<activityID>\w\w\w\w\w\w\w\w-\w\w\w\w-\w\w\w\w-\w\w\w\w-\w\w\w\w\w\w\w\w\w\w\w\w)'
            if ($found)
            {
                Write-Host 'Getting AppxLog' -NoNewline

                # Sometimes Get-AppxLog returns the old log (or nothing) when it's called too early after Add-AppxPackage
                for ($i = 0; $i -lt 5; $i++)
                {
                    Start-Sleep -S 1
                    Write-Host '.' -NoNewline
                }
                $activityLog = 'Activity log:' + (Get-AppxLog -ActivityId $matches['activityID'] | Out-String)
                $reportMessage += ' - More information in the activity log'

                Write-Host
            }
        }

        Send-ReportToHockeyApp $reportMessage $exception $activityLog

        exit $resultExceptionThrown
    }
    
    $filePath = Join-Path $scriptDir $systemartInstaller;
    if (Test-Path ($filePath))
    {
        Copy-Item  -Path $filePath -Destination (Split-Path -parent $scriptDir) -Force
    }

    if ($aaProcess)
    {
        Write-Host "Restarting computer..."
        Restart-Computer -Force
    }
    # Restart app if not in Assigned Access and app was running
    elseif ($appProcess)
    {
        Write-Host "Restarting app..."
        Start-Process -FilePath "explorer.exe" -ArgumentList "shell:AppsFolder\$($package.PackageFamilyName)!App"
    }
}

Function Test-WindowsVersion
{
    $version = (Get-WmiObject Win32_OperatingSystem).Version.Split(".")
    $major = $version[0] -as [int]
    $minor = $version[1] -as [int]
    $build = $version[2] -as [int]
    if (($major -lt $MinWinVersionMajor) -or (($minor -lt $MinWinVersionMinor) -and ($major -le $MinWinVersionMajor)) -or ($build -lt $MinWinVersionBuild))
    {
        throw [System.NotSupportedException] "Unsupported Windows version: $major.$minor.$build - required at least: $MinWinVersionMajor.$MinWinVersionMinor.$MinWinVersionBuild"
    }
}

Function WriteLine([System.IO.MemoryStream] $body, [String] $str)
{
    $encoded = [System.Text.Encoding]::ASCII.GetBytes($str + [Environment]::NewLine)
}

<#
    Install logeto-terminal product
#>

function Uninstall-LogetoTerminal 
{
    Write-LogetoDebug "Uninstalling terminal" 

    $package = Get-AppxPackage -Name $TerminalPackageName
    if ($package)
    {
        Stop-Process -Name $TerminalProcessName -ErrorAction SilentlyContinue
        Remove-AppxPackage $package                
    }  

    $keyname = "LogetoTerminal"
    $regname = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
    $fullpath = $regname + "\" + $keyname

    if (Test-Path $fullpath)
    {
        Remove-Item -Path $fullpath -Recurse
    }

    $value = Join-Path $AppFolderPath $TerminalProductName
    Remove-Item -Path $value -Recurse -ErrorAction SilentlyContinue

    # Unregister task
    $N = [Environment]::UserName
    $name = "Logeto Terminal Appx Update ($N)"
    $taskExists = (Get-ScheduledTask | Where-Object {$_.TaskName -eq $name })
    if ($taskExists)
    {    
        Write-LogetoDebug "Logeto Terminal Appx Update task revert" -Debug
        Write-LogetoProgress "Removing old appx task" "ScriptUnRegisterAppxUpdateTaskRemovingTask"
        UnRegister-ScheduledTask -TaskName $name -Confirm:$false
    } else
    {
        Write-LogetoDebug "Cannot uninstal Logeto Terminal Appx Update task" -Debug
    }
}

<#
    Return Json file from the URL for set product and scope. If version parameter is set it returns Json file only if higher version was found, otherwise returns null.
#>

function Set-LogetoTerminalEnvironment
{
    Param(
        [parameter(Mandatory=$true)]
        [ValidateSet('Sideloading', 'SystemartCertificate', 'Uninstall', 'LoopbackExemption')]
        $Components
    )

    # Allow sideloading
    if ($Components -eq 'Sideloading')
    {
        if (!(Get-IsElevated))
        {
            throw Get-LogetoCustomException "Allow sideloading can be run only as an admin!" "" $ResultExceptionThrown 0
        }

        Write-LogetoDebug "Allowing sideloading"

        $keyname = "AllowAllTrustedApps"
        $regname = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"

        if (Test-Path $regname)
        {
            $regvalue = (Get-ItemProperty -Path $regname).$keyname

            if ($regvalue -lt 1)
            {
                New-ItemProperty -Path $regname -Name $keyname -Value 1 -PropertyType DWORD -Force
            }
        }
    }

    # Allow uninstalling of an application
    if ($Components -eq 'Uninstall')
    {
        if (!(Get-IsElevated))
        {
            throw Get-LogetoCustomException "Adding support for uninstalling can be run only as an admin!" "" $ResultExceptionThrown 0
        }

        Write-LogetoDebug "Allowing uninstall"

        $keyname = "LogetoTerminal"
        $regname = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
        $fullpath = $regname + "\" + $keyname

        if (-Not (Test-Path $fullpath))
        {
            New-Item -Path $regname -Name $keyname   
        }

        New-ItemProperty -Path $fullpath -Name "DisplayName" -Value "Logeto Terminal" -Force

        $value = "$AppFolderPath\$TerminalProductName\Uninstall\" + $ExeFileName + " -uninstallAsCurrentUser"
        New-ItemProperty -Path $fullpath -Name "UninstallString" -Value $value -Force

        New-ItemProperty -Path $fullpath -Name "Publisher" -Value "Systemart s.r.o." -Force
        
        $value = "$AppFolderPath\$TerminalProductName\Uninstall\ProductLogo.ico"
        New-ItemProperty -Path $fullpath -Name "DisplayIcon" -Value $value -Force
    }

    # Install Systemart certificate
    if ($Components -eq 'SystemartCertificate')
    {
        if (!(Get-IsElevated))
        {
            throw Get-LogetoCustomException "Installing systemart certificate can be run only as an admin!" "" $ResultExceptionThrown 0
        }

        Write-LogetoDebug "Installing systemart certificate"

        $cert = "$AppFolderPath\$TerminalProductName\Install\SystemartCertificationAuthority.crt"
        Write-LogetoDebug "Installing certificate" "ScriptTerminalCertificateInstalling"
        Import-Certificate -FilePath $cert -CertStoreLocation Cert:\LocalMachine\Root
    }

    if ($Components -eq 'LoopbackExemption')
    {
        if (!(Get-IsElevated))
        {
            throw Get-LogetoCustomException "Allow sideloading can be run only as an admin!" "" $ResultExceptionThrown 0
        }

        Write-LogetoDebug "Allowing Loopback Exemption"

        $packageFamilyName = (Get-AppxPackage -Name "$TerminalPackageName").PackageFamilyName
        CheckNetIsolation LoopbackExempt -a -n="$packageFamilyName"
    }
}